🦄 Sync 2026-04-02 23:48:06

This commit is contained in:
github-actions[bot] 2026-04-02 23:48:06 +08:00
parent 89b55b091a
commit 133898bee4
35 changed files with 958 additions and 537 deletions

View File

@ -1244,6 +1244,7 @@ return view.extend({
so.modalonly = true;
so = ss.taboption('field_transport', form.ListValue, 'transport_xhttp_mode', _('XHTTP mode'));
so.value('auto', _('Auto'));
so.value('stream-one', _('stream-one'));
so.value('stream-up', _('stream-up'));
so.value('packet-up', _('packet-up'));

View File

@ -1953,7 +1953,7 @@ msgstr "混淆类型"
#: htdocs/luci-static/resources/view/fchomo/node.js:408
#: htdocs/luci-static/resources/view/fchomo/node.js:409
msgid "Obfuscated as %s"
msgstr "混淆为"
msgstr "混淆为%s"
#: htdocs/luci-static/resources/view/fchomo/global.js:880
msgid "One or more numbers in the range 0-63 separated by commas"

View File

@ -1953,7 +1953,7 @@ msgstr "混淆類型"
#: htdocs/luci-static/resources/view/fchomo/node.js:408
#: htdocs/luci-static/resources/view/fchomo/node.js:409
msgid "Obfuscated as %s"
msgstr "混淆為"
msgstr "混淆為%s"
#: htdocs/luci-static/resources/view/fchomo/global.js:880
msgid "One or more numbers in the range 0-63 separated by commas"

View File

@ -1714,7 +1714,7 @@ local current_node = map:get(section)
queryParam[decodeURIComponent(params[0]).toLowerCase()] = decodeURIComponent(params[1] || '');
}
}
if ((!queryParam.security || queryParam.security == "") && queryParam.sni && queryParam.sni != "") {
if (!queryParam.security || queryParam.security == "") {
queryParam.security = "tls";
}
if (queryParam.security) {

View File

@ -1490,7 +1490,7 @@ local function processData(szType, content, add_mode, group)
result.address = host_port
end
result.tls = "0"
if (not params.security or params.security == "") and params.sni and params.sni ~= "" then
if not params.security or params.security == "" then
params.security = "tls"
end
if params.security == "tls" or params.security == "reality" then

View File

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

View File

@ -459,17 +459,11 @@ function copy_node()
local uuid = api.gen_short_uuid()
uci:section(appname, "nodes", uuid)
for k, v in pairs(uci:get_all(appname, section)) do
local filter = k:find("%.")
if filter and filter == 1 then
else
xpcall(function()
uci:set(appname, uuid, k, v)
end,
function(e)
end)
if not k:match("^%.") and k ~= "group" then
if k == "remarks" then v = (v or "") .. "(1)" end
uci:set(appname, uuid, k, v)
end
end
uci:delete(appname, uuid, "group")
uci:set(appname, uuid, "add_mode", 1)
api.uci_save(uci, appname)
http.redirect(api.url("node_config", uuid))

View File

@ -95,7 +95,7 @@ current_node = current_node_id and m.uci:get_all(appname, current_node_id) or {}
-- Shunt Start
if (has_singbox or has_xray) and #nodes_table > 0 then
if #normal_list > 0 then
if #normal_list > 0 or #iface_list > 0 then
if current_node.protocol == "_shunt" then
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall2/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, {
@ -112,7 +112,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
})
end
else
local tips = s:taboption("Main", DummyValue, "tips", " ")
local tips = s:taboption("Main", DummyValue, "tips", " ")
tips.rawhtml = true
tips.cfgvalue = function(t, n)
return string.format('<a style="color: red">%s</a>', translate("There are no available nodes, please add or subscribe nodes first."))
@ -351,6 +351,9 @@ end
local o_node = s.fields["node"]
local o_socks = s2.fields["node"]
for k, v in pairs(nodes_table) do
if #normal_list == 0 and #iface_list == 0 then
break
end
o_node:value(v.id, v["remark"])
o_node.group[#o_node.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
o_socks:value(v.id, v["remark"])

View File

@ -9,6 +9,9 @@ if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(api.url("node_list"))
end
fs = require "nixio.fs"
formvalue_key = "cbid." .. appname .. "." .. arg[1] .. "."
local header = Template(appname .. "/node_config/header")
header.api = api
header.section = arg[1]
@ -61,12 +64,18 @@ o.write = function(self, section, value)
m:set(section, self.option, value)
end
local fs = require "nixio.fs"
local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/client/type/"
s.val = {}
s.val["type"] = m.uci:get(appname, arg[1], "type")
s.val["protocol"] = m.uci:get(appname, arg[1], "protocol")
if luci.http.formvalue("cbi.submit") == "1" then
local formvalue_type = luci.http.formvalue(formvalue_key .. "type")
if formvalue_type then
s.val["type"] = formvalue_type
end
end
o = s:option(ListValue, "type", translate("Type"))
local type_table = {}

View File

@ -127,9 +127,11 @@ o.validate = function(self, value)
return value:gsub("%s+", ""):gsub("%z", "")
end
o = s:option(Flag, "allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
o.default = "1"
o = s:option(Flag, "allowInsecure", translate("allowInsecure"))
o.default = "0"
o.rmempty = false
o.description = translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.") .. "<br>" ..
translate("Used when the node link does not include this parameter.")
o = s:option(ListValue, "filter_keyword_mode", translate("Filter keyword Mode"))
o.default = "5"

View File

@ -168,14 +168,17 @@ if has_xray then
o:depends("fragment", true)
o = s_xray:option(Value, "fragment_length", translate("Fragment Length"), translate("Fragmented packet length (byte)"))
o.datatype = "or(uinteger,portrange)"
o.default = "100-200"
o:depends("fragment", true)
o = s_xray:option(Value, "fragment_interval", translate("Fragment Interval"), translate("Fragmentation interval (ms)"))
o = s_xray:option(Value, "fragment_delay", translate("Fragment Delay"), translate("Fragmentation interval (ms)"))
o.datatype = "or(uinteger,portrange)"
o.default = "10-20"
o:depends("fragment", true)
o = s_xray:option(Value, "fragment_maxSplit", translate("Max Split"), translate("Limit the maximum number of splits."))
o.datatype = "or(uinteger,portrange)"
o.default = "100-200"
o:depends("fragment", true)
@ -225,22 +228,18 @@ if has_xray then
o = s_xray_noise:option(ListValue, "type", translate("Type"))
o:value("rand", "rand")
o:value("array", "array")
o:value("str", "str")
o:value("hex", "hex")
o:value("base64", "base64")
o = s_xray_noise:option(Value, "packet", translate("Packet"))
o = s_xray_noise:option(Value, "packet", translate("Packet | Rand Length"))
o.datatype = "minlength(1)"
o.rmempty = false
o = s_xray_noise:option(Value, "delay", translate("Delay (ms)"))
o.datatype = "or(uinteger,portrange)"
o.rmempty = false
o = s_xray_noise:option(ListValue, "applyTo", translate("IP Type"))
o:value("ip", "ALL")
o:value("ipv4", "IPv4")
o:value("ipv6", "IPv6")
end
if has_singbox then

View File

@ -20,9 +20,6 @@ local function _n(name)
return option_prefix .. name
end
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o:value("udp", "UDP")
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port"))
@ -67,7 +64,10 @@ o.rewrite_option = o.option
o = s:option(Value, _n("recv_window_conn"), translate("QUIC connection receive window"))
o.rewrite_option = o.option
o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s-120s)")
o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
o.rewrite_option = o.option
o = s:option(Value, _n("keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)")
o.rewrite_option = o.option
o = s:option(Flag, _n("disable_mtu_discovery"), translate("Disable MTU detection"))

View File

@ -25,7 +25,6 @@ local function _n(name)
return option_prefix .. name
end
local formvalue_key = "cbid." .. appname .. "." .. arg[1] .. "."
local formvalue_proto = luci.http.formvalue(formvalue_key .. _n("protocol"))
if formvalue_proto then s.val["protocol"] = formvalue_proto end
@ -326,7 +325,10 @@ 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_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 = 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"))
@ -368,7 +370,7 @@ o:depends({ [_n("protocol")] = "hysteria2" })
-- o:value("1.3")
-- o:depends({ [_n("tls")] = true })
o = s:option(Value, _n("tls_serverName"), translate("Domain"))
o = s:option(Value, _n("tls_serverName"), "SNI " .. translate("Domain"))
o:depends({ [_n("tls")] = true })
o:depends({ [_n("protocol")] = "hysteria2" })
@ -404,7 +406,7 @@ o.validate = function(self, value)
end
o = s:option(ListValue, _n("ech_ForceQuery"), translate("ECH Query Policy"), translate("Controls the policy used when performing DNS queries for ECH configuration."))
o.default = "none"
o.default = "full"
o:value("none")
o:value("half")
o:value("full")
@ -687,10 +689,12 @@ o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(TextValue, _n("finalmask"), " ", translate("An FinalMaskObject in JSON format, used for sharing."))
o = s:option(TextValue, _n("finalmask"), " ")
o:depends({ [_n("use_finalmask")] = true })
o.rows = 10
o.wrap = "off"
o.description = translate("An FinalMaskObject in JSON format, used for sharing.") .. "<br>" ..
translate("Custom finalmask overrides mkcp, hysteria2, fragment, noise, and related settings.")
o.custom_cfgvalue = function(self, section, value)
local raw = m:get(section, "finalmask")
if raw then

View File

@ -25,7 +25,6 @@ local function _n(name)
return option_prefix .. name
end
local formvalue_key = "cbid." .. appname .. "." .. arg[1] .. "."
local formvalue_proto = luci.http.formvalue(formvalue_key .. _n("protocol"))
if formvalue_proto then s.val["protocol"] = formvalue_proto end
@ -68,6 +67,9 @@ if singbox_tags:find("with_quic") then
end
o:value("anytls", "AnyTLS")
o:value("ssh", "SSH")
if singbox_tags:find("with_naive_outbound") then
o:value("naive", "NaïveProxy")
end
o:value("_urltest", translate("URLTest"))
o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface"))
@ -227,6 +229,7 @@ o = s:option(Value, _n("username"), translate("Username"))
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "ssh" })
o:depends({ [_n("protocol")] = "naive" })
o = s:option(Value, _n("password"), translate("Password"))
o.password = true
@ -238,6 +241,7 @@ o:depends({ [_n("protocol")] = "trojan" })
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "anytls" })
o:depends({ [_n("protocol")] = "ssh" })
o:depends({ [_n("protocol")] = "naive" })
o = s:option(ListValue, _n("security"), translate("Encrypt Method"))
for a, t in ipairs(security_list) do o:value(t) end
@ -285,6 +289,7 @@ end
o = s:option(Flag, _n("uot"), translate("UDP over TCP"))
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "naive" })
o = s:option(Value, _n("alter_id"), "Alter ID")
o.datatype = "uinteger"
@ -345,9 +350,6 @@ if singbox_tags:find("with_quic") then
o = s:option(Flag, _n("hysteria_disable_mtu_discovery"), translate("Disable MTU detection"))
o:depends({ [_n("protocol")] = "hysteria" })
o = s:option(Value, _n("hysteria_alpn"), translate("QUIC TLS ALPN"))
o:depends({ [_n("protocol")] = "hysteria" })
end
if singbox_tags:find("with_quic") then
@ -387,6 +389,8 @@ if singbox_tags:find("with_quic") then
o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:value("spdy/3.1")
o:value("h3,spdy/3.1")
o:depends({ [_n("protocol")] = "tuic" })
end
@ -437,6 +441,26 @@ o = s:option(Value, _n("ssh_client_version"), translate("Client Version"), trans
o:depends({ [_n("protocol")] = "ssh" })
-- [[ SSH config end ]] --
-- [[ naive start ]] --
o = s:option(Value, _n("naive_insecure_concurrency"), translate("Concurrent Tunnels"))
o.datatype = "uinteger"
o.placeholder = "0"
o.default = "0"
o:depends({ [_n("protocol")] = "naive" })
o = s:option(Flag, _n("naive_quic"), translate("QUIC"))
o.default = 0
o:depends({ [_n("protocol")] = "naive" })
o = s:option(ListValue, _n("naive_congestion_control"), translate("Congestion control algorithm"))
o.default = "bbr"
o:value("bbr", translate("BBR"))
o:value("bbr2", translate("BBRv2"))
o:value("cubic", translate("CUBIC"))
o:value("reno", translate("New Reno"))
o:depends({ [_n("naive_quic")] = "1" })
-- [[ naive end ]] --
o = s:option(Flag, _n("tls"), translate("TLS"))
o.default = 0
o:depends({ [_n("protocol")] = "vmess" })
@ -446,7 +470,7 @@ o:depends({ [_n("protocol")] = "trojan" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "anytls" })
o = s:option(ListValue, _n("alpn"), translate("alpn"))
o = s:option(ListValue, _n("alpn"), translate("ALPN"))
o.default = "default"
o:value("default", translate("Default"))
o:value("h3")
@ -456,6 +480,7 @@ o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true })
o:depends({ [_n("protocol")] = "hysteria" })
o = s:option(Flag, _n("tls_disable_sni"), translate("Disable SNI"), translate("Do not send server name in ClientHello."))
o.default = "0"
@ -463,14 +488,13 @@ o:depends({ [_n("tls")] = true })
o:depends({ [_n("protocol")] = "hysteria"})
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o = s:option(Value, _n("tls_serverName"), translate("Domain"))
o = s:option(Value, _n("tls_serverName"), "SNI " .. translate("Domain"))
o:depends({ [_n("tls")] = true })
o:depends({ [_n("protocol")] = "hysteria"})
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "naive" })
o = s:option(Flag, _n("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
o.default = "0"
@ -478,7 +502,6 @@ o:depends({ [_n("tls")] = true })
o:depends({ [_n("protocol")] = "hysteria"})
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o = s:option(Flag, _n("ech"), translate("ECH"))
o.default = "0"
@ -486,6 +509,7 @@ 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")] = "naive" })
o = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
o.default = ""
@ -501,6 +525,9 @@ o.validate = function(self, value)
return value
end
o = s:option(Value, _n("ech_query_server_name"), translate("ECH Query Domain"), translate("Overrides the domain name used for ECH HTTPS record queries."))
o:depends({ [_n("ech")] = true })
if singbox_tags:find("with_utls") then
o = s:option(Flag, _n("utls"), translate("uTLS"))
o.default = "0"
@ -666,6 +693,7 @@ o:depends({ [_n("tcp_guise")] = "http" })
o:depends({ [_n("transport")] = "http" })
o:depends({ [_n("transport")] = "ws" })
o:depends({ [_n("transport")] = "httpupgrade" })
o:depends({ [_n("protocol")] = "naive" })
-- [[ Mux ]]--
o = s:option(Flag, _n("mux"), translate("Mux"))

View File

@ -128,8 +128,18 @@ o.default = 0
o.rmempty = true
o.rewrite_option = o.option
o = s:option(DynamicList, _n("tls_alpn"), translate("TLS ALPN"))
o = s:option(ListValue, _n("tls_alpn"), translate("TLS ALPN"))
o.rmempty = true
o.default = ""
o:value("", translate("Default"))
o:value("h3")
o:value("h2")
o:value("h3,h2")
o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:value("spdy/3.1")
o:value("h3,spdy/3.1")
o.rewrite_option = o.option
api.luci_types(arg[1], m, s, type_name, option_prefix)

View File

@ -38,6 +38,7 @@ o:value("http", "HTTP")
o:value("socks", "Socks")
o:value("shadowsocks", "Shadowsocks")
o:value("trojan", "Trojan")
o:value("hysteria2", translate("Hysteria2"))
o:value("dokodemo-door", "dokodemo-door")
o:depends({ [_n("custom")] = false })
@ -123,6 +124,30 @@ o:value("", translate("Disable"))
o:value("xtls-rprx-vision")
o:depends({ [_n("protocol")] = "vless" })
---- [[ hysteria2 ]]
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"))
o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
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
o.validate = function(self, value, t)
@ -191,6 +216,7 @@ o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Flag, _n("use_mldsa65Seed"), translate("ML-DSA-65"))
o.default = "0"
@ -215,6 +241,7 @@ o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolu
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2"})
o.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
@ -230,6 +257,7 @@ o = s:option(FileUpload, _n("tls_keyFile"), translate("Private key absolute path
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria2"})
o.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
@ -402,7 +430,10 @@ o:depends({ [_n("custom")] = false })
--[[Fast Open]]
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ [_n("custom")] = false })
o:depends({ [_n("protocol")] = "vmess", [_n("custom")] = false })
o:depends({ [_n("protocol")] = "vless", [_n("custom")] = false })
o:depends({ [_n("protocol")] = "shadowsocks", [_n("custom")] = false })
o:depends({ [_n("protocol")] = "trojan", [_n("custom")] = false })
-- [[ Fallback ]]--
o = s:option(Flag, _n("fallback"), translate("Fallback"))

View File

@ -123,9 +123,6 @@ if singbox_tags:find("with_quic") then
o = s:option(Flag, _n("hysteria_disable_mtu_discovery"), translate("Disable MTU detection"))
o:depends({ [_n("protocol")] = "hysteria" })
o = s:option(Value, _n("hysteria_alpn"), translate("QUIC TLS ALPN"))
o:depends({ [_n("protocol")] = "hysteria" })
end
if singbox_tags:find("with_quic") then
@ -145,12 +142,26 @@ if singbox_tags:find("with_quic") then
o.default = "3"
o:depends({ [_n("protocol")] = "tuic" })
o = s:option(Value, _n("tuic_alpn"), translate("QUIC TLS ALPN"))
o = s:option(ListValue, _n("tuic_alpn"), translate("QUIC TLS ALPN"))
o.default = "default"
o:value("default", translate("Default"))
o:value("h3")
o:value("h2")
o:value("h3,h2")
o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:value("spdy/3.1")
o:value("h3,spdy/3.1")
o:depends({ [_n("protocol")] = "tuic" })
end
if singbox_tags:find("with_quic") then
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Commands the client to use the BBR flow control algorithm"))
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
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" })
@ -167,10 +178,6 @@ 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:depends({ [_n("protocol")] = "hysteria2"})
end
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol"))
@ -259,6 +266,18 @@ if singbox_tags:find("with_utls") then
o:depends({ [_n("reality")] = true })
end
o = s:option(ListValue, _n("alpn"), translate("ALPN"))
o.default = "default"
o:value("default", translate("Default"))
o:value("h3")
o:value("h2")
o:value("h3,h2")
o:value("http/1.1")
o:value("h2,http/1.1")
o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true, [_n("reality")] = false })
o:depends({ [_n("protocol")] = "hysteria" })
-- [[ TLS ]] --
o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")

View File

@ -1551,3 +1551,13 @@ function get_core(field, candidates)
end
return nil
end
function cleanEmptyTables(t)
if type(t) ~= "table" then return nil end
for k, v in pairs(t) do
if type(v) == "table" then
t[k] = cleanEmptyTables(v)
end
end
return next(t) and t or nil
end

View File

@ -87,14 +87,14 @@ function gen_config(var)
quic = {
initStreamReceiveWindow = (node.hysteria2_recv_window) and tonumber(node.hysteria2_recv_window) or nil,
initConnReceiveWindow = (node.hysteria2_recv_window_conn) and tonumber(node.hysteria2_recv_window_conn) or nil,
maxIdleTimeout = (function()
local timeoutStr = tostring(node.hysteria2_idle_timeout or "")
local timeout = tonumber(timeoutStr:match("^%d+"))
if timeout and timeout >= 4 and timeout <= 120 then
return tostring(timeout) .. "s"
end
return nil
end)(),
maxIdleTimeout = (function(t)
t = tonumber(tostring(t or "30"):match("^%d+"))
return (t and t >= 4 and t <= 120) and t .. "s" or "30s"
end)(node.hysteria2_idle_timeout),
keepAlivePeriod = (function(t)
t = tonumber(tostring(t or "0"):match("^%d+"))
return (t and t >= 2 and t <= 60) and t .. "s" or nil
end)(node.hysteria2_keep_alive_period),
disablePathMTUDiscovery = (node.hysteria2_disable_mtu_discovery) and true or false,
},
bandwidth = (node.hysteria2_up_mbps or node.hysteria2_down_mbps) and {

View File

@ -7,6 +7,7 @@ local appname = api.appname
local fs = api.fs
local CACHE_PATH = api.CACHE_PATH
local split = api.split
local ech_domain = {}
local local_version = api.get_app_version("sing-box"):match("[^v]+")
local version_ge_1_13_0 = api.compare_versions(local_version, ">=", "1.13.0")
@ -159,6 +160,9 @@ function gen_outbound(flag, node, tag, proxy_table)
}
local tls = nil
if node.protocol == "hysteria" or node.protocol == "hysteria2" or node.protocol == "tuic" or node.protocol == "naive" then
node.tls = "1"
end
if node.tls == "1" then
local alpn = nil
if node.alpn and node.alpn ~= "default" then
@ -176,10 +180,35 @@ function gen_outbound(flag, node, tag, proxy_table)
--max_version = "1.3",
fragment = fragment,
record_fragment = record_fragment,
ech = (node.ech == "1") and {
enabled = true,
config = node.ech_config and split(node.ech_config:gsub("\\n", "\n"), "\n") or {}
} or nil,
ech = (node.ech == "1") and (function()
local function get_ech_domain(s) --兼容xray "域名+DNS" 格式ech
local domain, dns = s:match("^([^+]+)%+(.+)$")
if not domain or not dns then return nil end
if not (dns:match("^https?://") or dns:match("^tcp://") or dns:match("^udp://") or dns:match("^h2c://")) then
return nil
end
if not domain:match("^[%w%-%.]+%.[%a]+$") then return nil end
return domain
end
local ech = { enabled = true }
local config = node.ech_config
local qname = node.ech_query_server_name
if config and not qname then
qname = get_ech_domain(config)
if not qname and not (config:match("%-+%s*BEGIN") and config:match("%-+%s*END")) then
config = "-----BEGIN ECH CONFIGS-----\n" .. config:gsub("%s+", "") .. "\n-----END ECH CONFIGS-----"
end
end
if qname then
ech.query_server_name = qname
ech_domain[qname] = true
elseif config then
ech.config = { config }
elseif node.tls_serverName and node.tls_serverName ~= "" then
ech_domain[node.tls_serverName] = true
end
return ech
end)() or nil,
utls = (node.utls == "1" or node.reality == "1") and {
enabled = true,
fingerprint = node.fingerprint or "chrome"
@ -418,20 +447,7 @@ function gen_outbound(flag, node, tag, proxy_table)
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,
tls = {
enabled = true,
server_name = node.tls_serverName,
insecure = (node.tls_allowInsecure == "1") and true or false,
fragment = fragment,
record_fragment = record_fragment,
alpn = (node.hysteria_alpn and node.hysteria_alpn ~= "") and {
node.hysteria_alpn
} or nil,
ech = (node.ech == "1") and {
enabled = true,
config = node.ech_config and split(node.ech_config:gsub("\\n", "\n"), "\n") or {}
} or nil
}
tls = tls
}
end
@ -452,22 +468,15 @@ function gen_outbound(flag, node, tag, proxy_table)
udp_over_stream = false,
zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1") and true or false,
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
tls = {
enabled = true,
disable_sni = (node.tls_disable_sni == "1") and true or false,
server_name = node.tls_serverName,
insecure = (node.tls_allowInsecure == "1") and true or false,
fragment = fragment,
record_fragment = record_fragment,
alpn = (node.tuic_alpn and node.tuic_alpn ~= "") and {
node.tuic_alpn
} or nil,
ech = (node.ech == "1") and {
enabled = true,
config = node.ech_config and split(node.ech_config:gsub("\\n", "\n"), "\n") or {},
} or nil
}
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
end
if node.protocol == "hysteria2" then
@ -494,17 +503,7 @@ function gen_outbound(flag, node, tag, proxy_table)
password = node.hysteria2_obfs_password
} or nil,
password = node.hysteria2_auth_password or nil,
tls = {
enabled = true,
server_name = node.tls_serverName,
insecure = (node.tls_allowInsecure == "1") and true or false,
fragment = fragment,
record_fragment = record_fragment,
ech = (node.ech == "1") and {
enabled = true,
config = node.ech_config and split(node.ech_config:gsub("\\n", "\n"), "\n") or {}
} or nil
}
tls = tls
}
end
@ -530,6 +529,23 @@ function gen_outbound(flag, node, tag, proxy_table)
}
end
if node.protocol == "naive" then
protocol_table = {
username = (node.username and node.username ~= "") and node.username or "",
password = (node.password and node.password ~= "") and node.password or "",
insecure_concurrency = tonumber(node.naive_insecure_concurrency or 0) > 0 and tonumber(node.naive_insecure_concurrency) or 0,
udp_over_tcp = node.uot == "1" and {
enabled = true,
version = 2
} or false,
extra_headers = node.user_agent and {
["User-Agent"] = node.user_agent
} or nil,
quic = node.naive_quic == "1" and true or false,
quic_congestion_control = (node.naive_quic == "1" and node.naive_congestion_control) and node.naive_congestion_control or nil
}
end
if protocol_table then
for key, value in pairs(protocol_table) do
result[key] = value
@ -548,6 +564,14 @@ function gen_config_server(node)
enabled = true,
certificate_path = node.tls_certificateFile,
key_path = node.tls_keyFile,
alpn = (node.alpn and node.alpn ~= "default") and (function()
local alpn = {}
string.gsub(node.alpn, '[^,]+', function(w)
table.insert(alpn, w)
end)
if #alpn > 0 then return alpn end
return nil
end)() or nil
}
if node.tls == "1" and node.reality == "1" then
@ -570,7 +594,7 @@ function gen_config_server(node)
if node.tls == "1" and node.ech == "1" then
tls.ech = {
enabled = true,
key = node.ech_key and split(node.ech_key:gsub("\\n", "\n"), "\n") or {},
key = node.ech_key and { node.ech_key } or nil
}
end
@ -751,9 +775,6 @@ function gen_config_server(node)
end
if node.protocol == "hysteria" then
tls.alpn = (node.hysteria_alpn and node.hysteria_alpn ~= "") and {
node.hysteria_alpn
} or nil
protocol_table = {
up = node.hysteria_up_mbps .. " Mbps",
down = node.hysteria_down_mbps .. " Mbps",
@ -776,9 +797,14 @@ function gen_config_server(node)
end
if node.protocol == "tuic" then
tls.alpn = (node.tuic_alpn and node.tuic_alpn ~= "") and {
node.tuic_alpn
} or nil
tls.alpn = (node.tuic_alpn and node.tuic_alpn ~= "default") and (function()
local alpn = {}
string.gsub(node.tuic_alpn, '[^,]+', function(w)
table.insert(alpn, w)
end)
if #alpn > 0 then return alpn end
return nil
end)() or nil
protocol_table = {
users = {
{
@ -789,7 +815,7 @@ function gen_config_server(node)
},
congestion_control = node.tuic_congestion_control or "cubic",
zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1") and true or false,
heartbeat = node.tuic_heartbeat .. "s",
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
tls = tls
}
end
@ -1913,15 +1939,28 @@ function gen_config(var)
if not dns then
dns = {
servers = {
{
type = "local",
tag = "direct"
}
}
servers = {{
type = "local",
tag = "direct"
}}
}
end
if next(ech_domain) ~= nil then
table.insert(dns.servers, {
tag = "ech-dns",
type = "https",
server = "223.5.5.5"
})
if not dns.rules then dns.rules = {} end
local domain = {}
for line, _ in pairs(ech_domain) do domain[#domain+1] = line end
table.insert(dns.rules, 1, {
domain = domain,
server = "ech-dns"
})
end
if COMMON.default_outbound_tag == "block" then
route.final = nil
table.insert(route.rules, {

View File

@ -25,11 +25,18 @@ function gen_config(var)
-- certificates = node.tuic_certificate and { node.tuic_certpath } or nil,
udp_relay_mode = node.tuic_udp_relay_mode,
congestion_control = node.tuic_congestion_control,
heartbeat = node.tuic_heartbeat .. "s",
timeout = node.tuic_timeout .. "s",
gc_interval = node.tuic_gc_interval .. "s",
gc_lifetime = node.tuic_gc_lifetime .. "s",
alpn = node.tuic_tls_alpn,
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
timeout = (tonumber(node.tuic_timeout) or 8) .. "s",
gc_interval = (tonumber(node.tuic_gc_interval) or 3) .. "s",
gc_lifetime = (tonumber(node.tuic_gc_lifetime) or 15) .. "s",
alpn = (node.tuic_tls_alpn and node.tuic_tls_alpn ~= "") and (function()
local alpn = {}
string.gsub(node.tuic_tls_alpn, '[^,]+', function(w)
table.insert(alpn, w)
end)
if #alpn > 0 then return alpn end
return nil
end)() or nil,
disable_sni = (node.tuic_disable_sni == "1"),
zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1"),
send_window = tonumber(node.tuic_send_window),

View File

@ -9,21 +9,6 @@ local CACHE_PATH = api.CACHE_PATH
local xray_version = api.get_app_version("xray")
local function get_noise_packets()
local noises = {}
uci:foreach(appname, "xray_noise_packets", function(n)
local noise = (n.enabled == "1") and {
type = n.type,
packet = n.packet,
delay = string.find(n.delay, "-") and n.delay or tonumber(n.delay),
applyTo = n.applyTo
} or nil
table.insert(noises, noise)
end)
if #noises == 0 then noises = nil end
return noises
end
local function get_domain_excluded()
local path = string.format("/usr/share/%s/domains_excluded", appname)
local content = fs.readfile(path)
@ -146,8 +131,7 @@ function gen_outbound(flag, node, tag, proxy_table)
sockopt = {
mark = 255,
tcpFastOpen = (node.tcp_fast_open == "1") and true or nil,
tcpMptcp = (node.tcpMptcp == "1") and true or nil,
dialerProxy = (fragment or noise) and "dialerproxy" or nil
tcpMptcp = (node.tcpMptcp == "1") and true or nil
},
network = node.transport,
security = node.stream_security,
@ -169,7 +153,7 @@ function gen_outbound(flag, node, tag, proxy_table)
return node.tls_CertByName
end)(),
echConfigList = (node.ech == "1") and node.ech_config or nil,
echForceQuery = (node.ech == "1") and (node.ech_ForceQuery or "none") or nil
echForceQuery = (node.ech == "1") and (node.ech_ForceQuery or "full") or nil
} or nil,
realitySettings = (node.stream_security == "reality") and {
serverName = node.tls_serverName,
@ -237,58 +221,31 @@ function gen_outbound(flag, node, tag, proxy_table)
path = node.xhttp_path or "/",
host = node.xhttp_host,
extra = (function()
local extra_tbl = {}
-- Parse xhttp_extra and perform simple fault tolerance.
local extra = {}
if node.xhttp_extra then
local success, parsed = pcall(jsonc.parse, api.base64Decode(node.xhttp_extra))
if success and parsed then
extra_tbl = parsed.extra or parsed
for k, v in pairs(extra_tbl) do
if (type(v) == "table" and next(v) == nil) or v == nil then
extra_tbl[k] = nil
end
end
local ok, parsed = pcall(jsonc.parse, api.base64Decode(node.xhttp_extra))
if ok and type(parsed) == "table" then
extra = parsed.extra or parsed
end
end
-- Handling User-Agent
if node.user_agent and node.user_agent ~= "" then
extra_tbl.headers = extra_tbl.headers or {}
if not extra_tbl.headers["User-Agent"] and not extra_tbl.headers["user-agent"] then
extra_tbl.headers["User-Agent"] = node.user_agent
extra.headers = extra.headers or {}
if not extra.headers["User-Agent"] and not extra.headers["user-agent"] then
extra.headers["User-Agent"] = node.user_agent
end
end
-- Clean up empty headers
if extra_tbl.headers and next(extra_tbl.headers) == nil then
extra_tbl.headers = nil
end
return next(extra_tbl) ~= nil and extra_tbl or nil
return api.cleanEmptyTables(extra)
end)()
} or nil,
hysteriaSettings = (node.transport == "hysteria") and {
version = 2,
auth = node.hysteria2_auth_password,
up = (node.hysteria2_up_mbps and tonumber(node.hysteria2_up_mbps)) and tonumber(node.hysteria2_up_mbps) .. "mbps" or nil,
down = (node.hysteria2_down_mbps and tonumber(node.hysteria2_down_mbps)) and tonumber(node.hysteria2_down_mbps) .. "mbps" or nil,
udphop = (node.hysteria2_hop) and {
port = string.gsub(node.hysteria2_hop, ":", "-"),
interval = (function()
local v = tonumber((node.hysteria2_hop_interval or "30s"):match("^%d+"))
return (v and v >= 5) and v or 30
end)()
} or nil,
maxIdleTimeout = (function()
local timeoutStr = tostring(node.hysteria2_idle_timeout or "")
local timeout = tonumber(timeoutStr:match("^%d+"))
if timeout and timeout >= 4 and timeout <= 120 then
return timeout
end
return 30
end)(),
disablePathMTUDiscovery = (node.hysteria2_disable_mtu_discovery) and true or false
auth = node.hysteria2_auth_password
} or nil,
finalmask = (function()
local finalmask
if node.transport == "mkcp" then
local finalmask = {}
local TP = node.transport
if TP == "mkcp" then
local map = {none = "none", srtp = "header-srtp", utp = "header-utp", ["wechat-video"] = "header-wechat",
dtls = "header-dtls", wireguard = "header-wireguard", dns = "header-dns"}
local udp = {}
@ -304,36 +261,55 @@ function gen_outbound(flag, node, tag, proxy_table)
c.settings = { password = node.mkcp_seed }
end
udp[#udp+1] = c
finalmask = { udp = udp }
elseif node.transport == "hysteria" and node.hysteria2_obfs_type and node.hysteria2_obfs_type ~= "" then
finalmask = {
udp = {{
finalmask.udp = udp
elseif TP == "hysteria" then
if node.hysteria2_obfs_type and node.hysteria2_obfs_type ~= "" then
finalmask.udp = {{
type = node.hysteria2_obfs_type,
settings = node.hysteria2_obfs_password and {
password = node.hysteria2_obfs_password
} or nil
}}
end
local up = tonumber(node.hysteria2_up_mbps) or 0
local down = tonumber(node.hysteria2_down_mbps) or 0
finalmask.quicParams = {
congestion = (up <= 0 and down <= 0) and "bbr" or "brutal",
brutalUp = up > 0 and (up .. "mbps") or nil,
brutalDown = down > 0 and (down .. "mbps") or nil,
udpHop = (node.hysteria2_hop) and {
ports = string.gsub(node.hysteria2_hop, ":", "-"),
interval = (function(v)
v = tonumber((v or "30s"):match("^%d+"))
return (v and v >= 5) and v or 30
end)(node.hysteria2_hop_interval)
} or nil,
maxIdleTimeout = (function(t)
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),
keepAlivePeriod = (function(t)
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),
disablePathMTUDiscovery = tonumber(node.hysteria2_disable_mtu_discovery) == 1
}
end
if fragment and fragment_table and ({raw=1, ws=1, httpupgrade=1, grpc=1, xhttp=1})[TP] then
finalmask.tcp = finalmask.tcp or {}
finalmask.tcp[#finalmask.tcp+1] = api.clone(fragment_table)
end
if noise and noise_table and (TP == "mkcp" or (TP == "xhttp" and node.alpn == "h3")) then
finalmask.udp = finalmask.udp or {}
finalmask.udp[#finalmask.udp+1] = api.clone(noise_table)
end
if node.finalmask and node.finalmask ~= "" then
local ok, fm = pcall(jsonc.parse, api.base64Decode(node.finalmask))
if ok and type(fm) == "table" then
if not finalmask or not next(finalmask) then
finalmask = fm
else
if type(fm.udp) == "table" then
finalmask.udp = finalmask.udp or {}
for i = 1, #fm.udp do
finalmask.udp[#finalmask.udp+1] = fm.udp[i]
end
end
if type(fm.tcp) == "table" then
finalmask.tcp = fm.tcp
end
end
finalmask = fm
end
end
return (finalmask and next(finalmask)) and finalmask or nil
return api.cleanEmptyTables(finalmask)
end)()
} or nil,
settings = {
@ -475,6 +451,13 @@ function gen_config_server(node)
clients = clients
}
end
elseif node.protocol == "hysteria2" then
settings = {
version = 2,
clients = node.hysteria2_auth_password and {
{ auth = node.hysteria2_auth_password }
}
}
elseif node.protocol == "dokodemo-door" then
settings = {
network = node.d_protocol,
@ -552,6 +535,12 @@ function gen_config_server(node)
end
end
if node.protocol == "hysteria2" then
node.protocol = "hysteria"
node.transport = "hysteria"
node.tls = "1"
end
local config = {
log = {
loglevel = ("1" == node.log) and node.loglevel or "none"
@ -618,8 +607,11 @@ function gen_config_server(node)
maxUploadSize = node.xhttp_maxuploadsize,
maxConcurrentUploads = node.xhttp_maxconcurrentuploads
} or nil,
hysteriaSettings = (node.transport == "hysteria") and {
version = 2
} or nil,
finalmask = (function()
local finalmask
local finalmask = {}
if node.transport == "mkcp" then
local map = {none = "none", srtp = "header-srtp", utp = "header-utp", ["wechat-video"] = "header-wechat",
dtls = "header-dtls", wireguard = "header-wireguard", dns = "header-dns"}
@ -636,27 +628,32 @@ function gen_config_server(node)
c.settings = { password = node.mkcp_seed }
end
udp[#udp+1] = c
finalmask = { udp = udp }
finalmask.udp = udp
elseif node.transport == "hysteria" then
if node.hysteria2_obfs_type and node.hysteria2_obfs_type ~= "" then
finalmask.udp = {{
type = node.hysteria2_obfs_type,
settings = node.hysteria2_obfs_password and {
password = node.hysteria2_obfs_password
} or nil
}}
end
local ignore = tonumber(node.hysteria2_ignore_client_bandwidth) == 1
local up = (not ignore) and tonumber(node.hysteria2_up_mbps) or 0
local down = (not ignore) and tonumber(node.hysteria2_down_mbps) or 0
finalmask.quicParams = {
congestion = (up <= 0 and down <= 0) and "bbr" or "brutal",
brutalUp = up > 0 and (up .. "mbps") or nil,
brutalDown = down > 0 and (down .. "mbps") or nil
}
end
if node.finalmask and node.finalmask ~= "" then
local ok, fm = pcall(jsonc.parse, api.base64Decode(node.finalmask))
if ok and type(fm) == "table" then
if not finalmask or not next(finalmask) then
finalmask = fm
else
if type(fm.udp) == "table" then
finalmask.udp = finalmask.udp or {}
for i = 1, #fm.udp do
finalmask.udp[#finalmask.udp+1] = fm.udp[i]
end
end
if type(fm.tcp) == "table" then
finalmask.tcp = fm.tcp
end
end
finalmask = fm
end
end
return (finalmask and next(finalmask)) and finalmask or nil
return api.cleanEmptyTables(finalmask)
end)(),
sockopt = {
tcpFastOpen = (node.tcp_fast_open == "1") and true or nil,
@ -751,6 +748,38 @@ function gen_config(var)
local xray_settings = uci:get_all(appname, "@global_xray[0]") or {}
if xray_settings.fragment == "1" then
local delay = xray_settings.fragment_delay
fragment_table = {
type = "fragment",
settings = {
packets = xray_settings.fragment_packets,
length = xray_settings.fragment_length,
delay = delay and (delay:find("-", 1, true) and delay or tonumber(delay)) or nil,
maxSplit = xray_settings.fragment_maxSplit
}
}
end
if xray_settings.noise == "1" then
local noises = {}
uci:foreach(appname, "xray_noise_packets", function(n)
if n.enabled == "1" then
local noise = {
rand = (n.type == "rand" and n.packet) and (n.packet:find("-", 1, true) and n.packet or tonumber(n.packet)) or nil,
type = (n.type ~= "rand") and n.type or nil,
packet = (n.type ~= "rand") and n.packet or nil,
delay = n.delay and (n.delay:find("-", 1, true) and n.delay or tonumber(n.delay)) or nil
}
table.insert(noises, noise)
end
end)
noise_table = #noises > 0 and {
type = "noise",
settings = { reset = 0, noise = noises }
} or nil
end
local node = node_id and uci:get_all(appname, node_id) or nil
local balancers = {}
local rules = {}
@ -909,7 +938,7 @@ function gen_config(var)
end
end
if is_new_blc_node then
local outboundTag = gen_outbound_get_tag(flag, blc_node_id, blc_node_tag, { fragment = xray_settings.fragment == "1" or nil, noise = xray_settings.record_fragment == "1" or nil, run_socks_instance = not no_run })
local outboundTag = gen_outbound_get_tag(flag, blc_node_id, blc_node_tag, { fragment = xray_settings.fragment == "1" or nil, noise = xray_settings.noise == "1" or nil, run_socks_instance = not no_run })
if outboundTag then
valid_nodes[#valid_nodes + 1] = outboundTag
end
@ -934,7 +963,7 @@ function gen_config(var)
local fallback_node = get_node_by_id(fallback_node_id)
if fallback_node then
if fallback_node.protocol ~= "_balancing" then
local outboundTag = gen_outbound_get_tag(flag, fallback_node, fallback_node_id, { fragment = xray_settings.fragment == "1" or nil, noise = xray_settings.record_fragment == "1" or nil, run_socks_instance = not no_run })
local outboundTag = gen_outbound_get_tag(flag, fallback_node, fallback_node_id, { fragment = xray_settings.fragment == "1" or nil, noise = xray_settings.noise == "1" or nil, run_socks_instance = not no_run })
if outboundTag then
fallback_node_tag = outboundTag
end
@ -964,27 +993,29 @@ function gen_config(var)
strategy = strategy
})
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then
local t = api.format_go_time(_node.probeInterval)
if t == "0s" then
t = "60s"
elseif not t:find("[hm]") and tonumber(t:match("%d+")) < 10 then
t = "10s"
end
if _node.balancingStrategy == "leastLoad" then
if not burstObservatory then
burstObservatory = {
subjectSelector = { "blc-" },
pingConfig = {
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
sampling = 3,
timeout = "5s"
}
burstObservatory = burstObservatory or {
subjectSelector = { "blc-" },
pingConfig = {
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
interval = t,
sampling = 3,
timeout = "5s"
}
end
}
else
if not observatory then
observatory = {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
enableConcurrency = true
}
end
observatory = observatory or {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
probeInterval = t,
enableConcurrency = true
}
end
end
local loopback_outbound = gen_loopback(loopback_tag, loopback_dst)
@ -1731,28 +1762,6 @@ function gen_config(var)
}
}
if xray_settings.fragment == "1" or xray_settings.noise == "1" then
table.insert(outbounds, {
protocol = "freedom",
tag = "dialerproxy",
settings = {
domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP",
fragment = (xray_settings.fragment == "1") and {
packets = (xray_settings.fragment_packets and xray_settings.fragment_packets ~= "") and xray_settings.fragment_packets,
length = (xray_settings.fragment_length and xray_settings.fragment_length ~= "") and xray_settings.fragment_length,
interval = (xray_settings.fragment_interval and xray_settings.fragment_interval ~= "") and xray_settings.fragment_interval,
maxSplit = (xray_settings.fragment_maxSplit and xray_settings.fragment_maxSplit ~= "") and xray_settings.fragment_maxSplit
} or nil,
noises = (xray_settings.noise == "1") and get_noise_packets() or nil
},
streamSettings = {
sockopt = {
mark = 255
}
}
})
end
local direct_outbound = {
protocol = "freedom",
tag = "direct",

View File

@ -2,8 +2,6 @@
local api = self.api
-%>
<script type="text/javascript">
let node_id = "<%=self.section%>";
let url = '<%=api.url("node_config")%>/' + node_id;
//<![CDATA[
document.addEventListener("DOMContentLoaded", function () {
waitForElement('select[name*="<%=api.appname%>"][name*="type"]', function(el) {
@ -13,8 +11,12 @@ local api = self.api
if (o_val != el.value) {
let save = true;
if (save) {
//document.getElementsByClassName('cbi-button-save')[0].click();
update_node({
type: getOption("type").value
remarks: getOption("remarks").value,
group: getOption("group").value,
type: getOption("type").value,
protocol: ''
});
}
}
@ -29,11 +31,14 @@ local api = self.api
let name = name_split[name_split.length - 1];
let save = true;
if (save) {
//document.getElementsByClassName('cbi-button-save')[0].click();
update_node({
remarks: getOption("remarks").value,
group: getOption("group").value,
protocol: getOption(name).value
});
} else {
window.location.href = url + "?select_proto=" + el.value;
window.location.href = node_config_url + "?select_proto=" + el.value;
}
}
});

View File

@ -3,6 +3,9 @@ local api = self.api
-%>
<script type="text/javascript">
//<![CDATA[
let node_id = "<%=self.section%>";
let node_config_url = '<%=api.url("node_config")%>/' + node_id;
function waitForElement(selector, callback) {
const el = document.querySelector(selector);
if (el) return callback(el);
@ -18,7 +21,7 @@ local api = self.api
function getOption(opt) {
var obj;
var id = 'cbid.<%=api.appname%>.<%=self.section%>.' + opt;
var id = 'cbid.<%=api.appname%>.' + node_id + '.' + opt;
obj = document.getElementsByName(id)[0] || document.getElementById(id);
if (obj) {
var combobox = document.getElementById('cbi.combobox.' + id);
@ -37,12 +40,12 @@ local api = self.api
function update_node(data) {
XHR.get('<%=api.url("update_node")%>', {
id: "<%=self.section%>",
id: node_id,
data: JSON.stringify(data)
},
function(x, data) {
if (x && x.status == 200 && data.code == 1) {
window.location.reload();
window.location.href = node_config_url;
}
else {
alert("<%:Error%>");

View File

@ -675,6 +675,31 @@ local current_node = map:get(section)
params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure");
}
params += "#" + encodeURI(v_alias.value);
if (params[0] == "&") {
params = params.substring(1);
}
url += params;
} else if (v_type === "sing-box" && opt.get(dom_prefix + "protocol").value === "naive") {
protocol = "naive+https";
var v_username = opt.get(dom_prefix + "username");
var v_password = opt.get(dom_prefix + "password");
var v_port = opt.get(dom_prefix + "port");
url = encodeURIComponent(v_username.value) +
":" + encodeURIComponent(v_password.value) +
"@" + _address +
":" + v_port.value + "?";
var params = "security=tls";
params += opt.query("sni", dom_prefix + "tls_serverName");
params += opt.query("insecure-concurrency", dom_prefix + "naive_insecure_concurrency");
params += opt.query("ech", dom_prefix + "ech_config");
params += opt.query("uot", dom_prefix + "uot");
if (opt.get(dom_prefix + "naive_quic")?.checked) {
protocol = "naive+quic";
params += opt.query("congestion_control", dom_prefix + "naive_congestion_control");
}
params += "#" + encodeURI(v_alias.value);
if (params[0] == "&") {
params = params.substring(1);
@ -1034,10 +1059,10 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'reality', false);
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'tls_allowInsecure', true);
if ((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0') {
opt.set(dom_prefix + 'tls_allowInsecure', false);
}
opt.set(
dom_prefix + 'tls_allowInsecure',
!((queryParam.allowinsecure ?? '0') === '0' && (queryParam.allowInsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0')
);
if (queryParam.fp && queryParam.fp.trim() != "") {
opt.set(dom_prefix + 'utls', true);
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
@ -1205,7 +1230,7 @@ local current_node = map:get(section)
var params;
for (var i = 0; i < queryArray.length; i++) {
params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0]).toLowerCase()] = decodeURIComponent(params[1] || '');
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
}
}
opt.set(dom_prefix + 'mux', queryParam.mux === '1');
@ -1281,21 +1306,15 @@ local current_node = map:get(section)
queryParam.security = queryParam.security || "tls";
opt.set(dom_prefix + 'tls', queryParam.security === "tls");
if (queryParam.security === "tls") {
var tls_serverName = queryParam.peer;
if (queryParam.sni) {
tls_serverName = queryParam.sni
}
tls_serverName = tls_serverName || "";
opt.set(dom_prefix + 'tls_serverName', tls_serverName);
opt.set(dom_prefix + 'tls_serverName', queryParam.peer || queryParam.sni || "");
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_CertSha', queryParam.pcs || '');
opt.set(dom_prefix + 'tls_CertByName', queryParam.vcn || '');
}
opt.set(dom_prefix + 'tls_allowInsecure', true);
if ((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0') {
opt.set(dom_prefix + 'tls_allowInsecure', false);
}
opt.set(
dom_prefix + 'tls_allowInsecure',
!((queryParam.allowinsecure ?? '0') === '0' && (queryParam.allowInsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0')
);
opt.set(dom_prefix + 'tcp_fast_open', queryParam.tfo);
opt.set(dom_prefix + 'use_finalmask', !!queryParam.fm);
opt.set(dom_prefix + 'finalmask', queryParam.fm || "");
@ -1327,11 +1346,11 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'uuid', ssm.id);
opt.set(dom_prefix + 'tls', ssm.tls === "tls");
if (ssm.tls === "tls") {
var tls_serverName = ssm.host;
if (ssm.sni) {
tls_serverName = ssm.sni
}
opt.set(dom_prefix + 'tls_serverName', tls_serverName);
opt.set(dom_prefix + 'tls_serverName', ssm.sni || ssm.host);
opt.set(
dom_prefix + 'tls_allowInsecure',
!((ssm.allowinsecure ?? '0') === '0' && (ssm.allowInsecure ?? '0') === '0' && (ssm.insecure ?? '0') === '0')
);
}
ssm.net = ssm.net.toLowerCase();
if (ssm.net === "kcp" || ssm.net === "mkcp")
@ -1443,10 +1462,10 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'reality', false);
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'tls_allowInsecure', true);
if ((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0') {
opt.set(dom_prefix + 'tls_allowInsecure', false);
}
opt.set(
dom_prefix + 'tls_allowInsecure',
!((queryParam.allowinsecure ?? '0') === '0' && (queryParam.allowInsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0')
);
if (queryParam.fp && queryParam.fp.trim() != "") {
opt.set(dom_prefix + 'utls', true);
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
@ -1604,9 +1623,10 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'port', m.port || "443");
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || "");
if (queryParam.insecure && queryParam.insecure == "1") {
opt.set(dom_prefix + 'tls_allowInsecure', true);
}
opt.set(
dom_prefix + 'tls_allowInsecure',
!((queryParam.allowinsecure ?? '0') === '0' && (queryParam.allowInsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0')
);
opt.set(dom_prefix + 'tls_CertSha', queryParam.pcs || '');
opt.set(dom_prefix + 'tls_CertByName', queryParam.vcn || '');
if (m.hash) {
@ -1646,7 +1666,7 @@ local current_node = map:get(section)
var params;
for (i = 0; i < queryArray.length; i++) {
params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
queryParam[decodeURIComponent(params[0]).toLowerCase()] = decodeURIComponent(params[1] || '');
}
}
opt.set(dom_prefix + 'tuic_congestion_control', queryParam.congestion_control || 'cubic');
@ -1654,10 +1674,10 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'tls_disable_sni', queryParam.disable_sni === "1");
opt.set(dom_prefix + 'tls_allowInsecure', true);
if ((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0') {
opt.set(dom_prefix + 'tls_allowInsecure', false);
}
opt.set(
dom_prefix + 'tls_allowInsecure',
!((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0' && (queryParam.allow_insecure ?? '0') === '0')
);
if (hash) {
opt.set('remarks', decodeURIComponent(hash.substr(1)));
}
@ -1685,10 +1705,10 @@ local current_node = map:get(section)
var params;
for (i = 0; i < queryArray.length; i++) {
params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
queryParam[decodeURIComponent(params[0]).toLowerCase()] = decodeURIComponent(params[1] || '');
}
}
if ((!queryParam.security || queryParam.security == "") && queryParam.sni && queryParam.sni != "") {
if (!queryParam.security || queryParam.security == "") {
queryParam.security = "tls";
}
if (queryParam.security) {
@ -1697,10 +1717,7 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'reality', false);
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'tls_allowInsecure', true);
if ((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0') {
opt.set(dom_prefix + 'tls_allowInsecure', false);
}
opt.set(dom_prefix + 'tls_allowInsecure', !((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0'));
if (queryParam.fp && queryParam.fp.trim() != "") {
opt.set(dom_prefix + 'utls', true);
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
@ -1723,6 +1740,55 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "naive+https" || ssu[0] === "naive+quic") {
if (has_singbox) {
dom_prefix = "singbox_"
opt.set('type', "sing-box");
}
opt.set(dom_prefix + 'protocol', "naive");
var _parsedUrl = new URL("http://" + ssu[1]);
var username = _parsedUrl.username;
var password = _parsedUrl.password;
var hostname = _parsedUrl.hostname;
var port = _parsedUrl.port;
var search = _parsedUrl.search;
var hash = _parsedUrl.hash;
if (!username || !password) { //某些链接会把username和password之间的:进行编码
const decoded = decodeURIComponent(username || password || "");
const i = decoded.indexOf(":");
if (i > -1) {
username = decoded.slice(0, i);
password = decoded.slice(i + 1);
}
}
opt.set(dom_prefix + 'username', decodeURIComponent(username));
opt.set(dom_prefix + 'password', decodeURIComponent(password));
opt.set(dom_prefix + 'address', unbracketIP(hostname));
opt.set(dom_prefix + 'port', port || "443");
var queryParam = {};
if (search.length > 1) {
var query = search.split('?')
var queryParams = query[1];
var queryArray = queryParams.split('&');
var params;
for (i = 0; i < queryArray.length; i++) {
params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
}
}
opt.set(dom_prefix + 'naive_insecure_concurrency', queryParam['insecure-concurrency'] || '0');
opt.set(dom_prefix + 'uot', (queryParam.uot ?? '0') === '1');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'ech', !!queryParam.ech);
opt.set(dom_prefix + 'ech_config', queryParam.ech || '');
if (ssu[0] === "naive+quic") {
opt.set(dom_prefix + 'naive_quic', true);
opt.set(dom_prefix + 'naive_congestion_control', queryParam.congestion_control || 'bbr');
}
if (hash) {
opt.set('remarks', decodeURIComponent(hash.substr(1)));
}
}
if (dom_prefix && dom_prefix != null) {
if (opt.get(dom_prefix + 'port').value) {
opt.get(dom_prefix + 'port').focus();

View File

@ -5,6 +5,10 @@ local api = require "luci.passwall2.api"
<script type="text/javascript">
//<![CDATA[
function ajax_add_node(link, group) {
const dom = document.getElementById('modal-mask');
dom.style.display = 'block';
dom.innerHTML = '<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff;"><%:Processing, please wait…%></div>';
const chunkSize = 1000; // Fragmented sending to overcome uhttpd's limitations, with each fragment containing 1000 characters.
const totalChunks = Math.ceil(link.length / chunkSize);
let currentChunk = 0;
@ -19,11 +23,15 @@ local api = require "luci.passwall2.api"
currentChunk++;
sendNextChunk();
} else {
dom.innerHTML = '';
dom.style.display = 'none';
alert("<%:Error%>");
return;
}
};
xhr.onerror = function () {
dom.innerHTML = '';
dom.style.display = 'none';
alert("<%:Network Error%>");
return;
};
@ -40,6 +48,22 @@ local api = require "luci.passwall2.api"
sendNextChunk();
}
function decodeIfBase64(str) {
try {
let s = str.replace(/-/g, '+').replace(/_/g, '/');
while (s.length % 4) s += '=';
const decoded = decodeURIComponent(
atob(s).split('').map(c =>
'%' + c.charCodeAt(0).toString(16).padStart(2, '0')
).join('')
);
if (btoa(unescape(encodeURIComponent(decoded))).replace(/=+$/, '') === s.replace(/=+$/, '')) {
return decoded;
}
} catch (e) {}
return str;
}
function getBg(el) {
if (!el) return null;
const style = getComputedStyle(el);
@ -74,15 +98,15 @@ local api = require "luci.passwall2.api"
const group = (document.querySelector('#addlink_group_custom input[type="hidden"]')?.value || "default");
nodes_link = nodes_link.replace(/\t/g, "").replace(/\r\n|\r/g, "\n").replace(/<[^>]*>/g, '').trim();
if (nodes_link != "") {
nodes_link = decodeIfBase64(nodes_link);
let s = nodes_link.split('://');
if (s.length > 1) {
close_add_link_div();
ajax_add_node(nodes_link, group);
}
else {
} else {
alert("<%:Please enter the correct link.%>");
}
}
else {
} else {
document.getElementById("nodes_link").focus();
}
}
@ -93,8 +117,7 @@ local api = require "luci.passwall2.api"
function(x, data) {
if(x && x.status == 200) {
window.location.href = '<%=api.url("node_list")%>';
}
else {
} else {
alert("<%:Error%>");
}
});
@ -154,8 +177,7 @@ local api = require "luci.passwall2.api"
function(x, data) {
if (x && x.status == 200) {
window.location.href = '<%=api.url("node_list")%>';
}
else {
} else {
alert("<%:Error%>");
}
});

View File

@ -48,8 +48,11 @@ table td, .table .td {
background: #5e72e445 !important;
}
.ping a:hover{
text-decoration : underline;
.ping a,
.ping_value a,
.tcping_value a {
text-decoration: none;
cursor: pointer;
}
@media (prefers-color-scheme: dark) {
@ -413,11 +416,16 @@ table td, .table .td {
}
}
if (ids.length > 0) {
if (confirm('<%:Are you sure to delete select nodes?%>') == true){
const dom = document.getElementById('modal-mask');
if (confirm('<%:Are you sure to delete select nodes?%>') == true) {
dom.style.display = 'block';
dom.innerHTML = '<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff;"><%:Processing, please wait…%></div>';
XHR.get('<%=api.url("delete_select_nodes")%>', {
ids: ids.join()
},
function(x, data) {
dom.innerHTML = '';
dom.style.display = 'none';
if (x && x.status == 200) {
/*
for (var i = 0 ; i < ids.length; i++) {
@ -427,8 +435,7 @@ table td, .table .td {
*/
ajaxAbortAll();
window.location.href = '<%=api.url("node_list")%>';
}
else {
} else {
alert("<%:Error%>");
}
});
@ -553,11 +560,10 @@ table td, .table .td {
text = "<%:Timeout%>";
} else {
let use_time = parseInt(result.use_time);
if (use_time < 1000) {
if (use_time < 1000)
color = "green";
} else if (use_time < 2000) {
else if (use_time < 2000)
color = "#fb9a05";
}
text = use_time + " ms";
}
}
@ -569,9 +575,11 @@ table td, .table .td {
}
function ping_node(cbi_id, dom, type) {
var full = get_address_full(cbi_id);
if (dom.dataset.busy == "1" || !cbi_id) return;
const full = get_address_full(cbi_id);
if ((type == "icmp" && full.address != "" ) || (type == "tcping" && full.address != "" && full.port != "")) {
dom.onclick = null
dom.dataset.busy = "1";
dom.style.color = "";
dom.innerText = "<%:Check...%>";
XHR.get('<%=api.url("ping_node")%>', {
address: full.address,
@ -579,19 +587,23 @@ table td, .table .td {
type: type
},
function(x, result) {
let color = "red";
let text = "<%:Error%>";
if(x && x.status == 200) {
if (result.ping == null || result.ping.trim() == "") {
dom.outerHTML = "<font style='color:red'><%:Timeout%></font>";
text = "<%:Timeout%>";
} else {
var ping = parseInt(result.ping);
if (ping < 100)
dom.outerHTML = "<font style='color:green'>" + result.ping + " ms" + "</font>";
color = "green";
else if (ping < 200)
dom.outerHTML = "<font style='color:#fb9a05'>" + result.ping + " ms" + "</font>";
else if (ping >= 200)
dom.outerHTML = "<font style='color:red'>" + result.ping + " ms" + "</font>";
color = "#fb9a05";
text = result.ping + " ms";
}
}
dom.innerText = text;
dom.style.color = color;
dom.dataset.busy = "0";
}
);
}
@ -601,15 +613,15 @@ table td, .table .td {
function pingAllNodes() {
if (auto_detection_time == "icmp" || auto_detection_time == "tcping") {
const now = Date.now();
var nodes = [];
const nodes = [];
const ping_value = document.getElementsByClassName(auto_detection_time == "tcping" ? 'tcping_value' : 'ping_value');
for (var i = 0; i < ping_value.length; i++) {
var cbi_id = ping_value[i].getAttribute("cbiid");
var full = get_address_full(cbi_id);
for (let i = 0; i < ping_value.length; i++) {
const cbi_id = ping_value[i].getAttribute("cbiid");
const full = get_address_full(cbi_id);
if ((auto_detection_time == "icmp" && full.address != "" ) || (auto_detection_time == "tcping" && full.address != "" && full.port != "")) {
var flag = false;
let flag = false;
// Merge duplicates
for (var j = 0; j < nodes.length; j++) {
for (let j = 0; j < nodes.length; j++) {
if (nodes[j].address == full.address && nodes[j].port == full.port) {
nodes[j].indexs = nodes[j].indexs + "," + i;
flag = true;
@ -621,12 +633,17 @@ table td, .table .td {
const cacheData = JSON.parse(localStorage.getItem(auto_detection_time + ":" + full.address + ":" + full.port));
if (cacheData && cacheData.savetime && (now - cacheData.timestamp) < cacheData.savetime) {
const a = ping_value[i].firstElementChild;
let color = "red";
if (cacheData.value < 100)
ping_value[i].innerHTML = "<font style='color:green'>" + cacheData.value + " ms" + "</font>";
color = "green";
else if (cacheData.value < 200)
ping_value[i].innerHTML = "<font style='color:#fb9a05'>" + cacheData.value + " ms" + "</font>";
else if (cacheData.value >= 200)
ping_value[i].innerHTML = "<font style='color:red'>" + cacheData.value + " ms" + "</font>";
color = "#fb9a05";
a.innerText = cacheData.value + " ms";
a.style.color = color;
a.onclick = function () {
ping_node(cbi_id, this, auto_detection_time);
};
} else {
localStorage.removeItem(auto_detection_time + ":" + full.address + ":" + full.port);
nodes.push({
@ -650,12 +667,14 @@ table td, .table .td {
},
function(x, result) {
if (x && x.status == 200) {
var strs = dom.indexs.split(",");
for (var i = 0; i < strs.length; i++) {
if (result.ping == null || result.ping.trim() == "") {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
} else {
var ping = parseInt(result.ping);
let strs = dom.indexs.split(",");
for (let i = 0; i < strs.length; i++) {
const a = ping_value[strs[i]].firstElementChild;
const cbi_id = ping_value[strs[i]].getAttribute("cbiid");
let color = "red";
let text = "<%:Timeout%>";
if (result.ping !== null && result.ping.trim() !== "") {
let ping = parseInt(result.ping);
//save to cache
const cache_data = {
dom_id: strs[i],
@ -663,23 +682,33 @@ table td, .table .td {
savetime: 60 * 1000,
value: ping
};
text = result.ping + " ms";
localStorage.setItem(auto_detection_time + ":" + dom.address + ":" + dom.port, JSON.stringify(cache_data));
if (ping < 100)
ping_value[strs[i]].innerHTML = "<font style='color:green'>" + result.ping + " ms" + "</font>";
color = "green";
else if (ping < 200)
ping_value[strs[i]].innerHTML = "<font style='color:#fb9a05'>" + result.ping + " ms" + "</font>";
else if (ping >= 200)
ping_value[strs[i]].innerHTML = "<font style='color:red'>" + result.ping + " ms" + "</font>";
color = "#fb9a05";
}
a.innerText = text;
a.style.color = color;
a.onclick = function () {
ping_node(cbi_id, this, auto_detection_time);
};
}
}
res();
},
5000,
function(x) {
var strs = dom.indexs.split(",");
for (var i = 0; i < strs.length; i++) {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
let strs = dom.indexs.split(",");
for (let i = 0; i < strs.length; i++) {
const a = ping_value[strs[i]].firstElementChild;
const cbi_id = ping_value[strs[i]].getAttribute("cbiid");
a.innerText = "<%:Timeout%>";
a.style.color = "red";
a.onclick = function () {
ping_node(cbi_id, this, auto_detection_time);
};
}
res();
}
@ -927,12 +956,12 @@ table td, .table .td {
if (auto_detection_time != "icmp" && o["address"] && o["port"]) {
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'{{id}}\', this, \'icmp\')"><%:Test%></a></span>');
} else {
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping_value" cbiid="{{id}}">---</span>');
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>');
}
if (auto_detection_time != "tcping" && o["address"] && o["port"]) {
innerHTML = innerHTML.split("{{tcping}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'{{id}}\', this, \'tcping\')"><%:Test%></a></span>');
} else {
innerHTML = innerHTML.split("{{tcping}}").join('<span class="tcping_value" cbiid="{{id}}">---</span>');
innerHTML = innerHTML.split("{{tcping}}").join('<span class="tcping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>');
}
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:urltest_node(\'{{id}}\', this)"><%:Test%></a></span>');
innerHTML = innerHTML.split("{{id}}").join(o[".name"]);

View File

@ -567,6 +567,9 @@ msgstr "نوع احراز هویت"
msgid "Auth Password"
msgstr "رمز عبور احراز هویت"
msgid "Client BBR Flow Control"
msgstr "کنترل BBR مشتری"
msgid "Commands the client to use the BBR flow control algorithm"
msgstr "به کلاینت دستور می‌دهد تا از الگوریتم کنترل جریان BBR استفاده کند"
@ -591,6 +594,9 @@ msgstr "پنجره دریافت اتصال QUIC"
msgid "QUIC concurrent bidirectional streams"
msgstr "جریان‌های دوطرفه همزمان QUIC"
msgid "QUIC KeepAlive interval"
msgstr "فاصله QUIC KeepAlive"
msgid "Disable MTU detection"
msgstr "غیرفعال کردن تشخیص MTU"
@ -1335,6 +1341,9 @@ msgstr "اجازه ناامن (allowInsecure)"
msgid "Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."
msgstr "آیا اتصالات ناامن مجاز هستند. در صورت تیک خوردن، اعتبارسنجی گواهی نادیده گرفته می‌شود."
msgid "Used when the node link does not include this parameter."
msgstr "زمانی استفاده می‌شود که لینک گره شامل این پارامتر نباشد."
msgid "%s Node Use Type"
msgstr "نوع استفاده گره %s"
@ -1353,6 +1362,9 @@ msgstr "رمز عبور TUIC برای Socks محلی"
msgid "UDP relay mode"
msgstr "حالت بازپخش UDP"
msgid "Concurrent Tunnels"
msgstr "تونل‌های همزمان"
msgid "Congestion control algorithm"
msgstr "الگوریتم کنترل ازدحام"
@ -1674,12 +1686,21 @@ msgstr "سیاست پرس‌وجوی ECH"
msgid "Controls the policy used when performing DNS queries for ECH configuration."
msgstr "سیاستی که هنگام انجام پرس‌وجوهای DNS برای پیکربندی ECH استفاده می‌شود را کنترل می‌کند."
msgid "ECH Query Domain"
msgstr "دامنه پرس و جو ECH"
msgid "Overrides the domain name used for ECH HTTPS record queries."
msgstr "نام دامنه مورد استفاده برای پرس‌وجوهای رکورد ECH HTTPS را لغو می‌کند."
msgid "Enable Multipath TCP, need to be enabled in both server and client configuration."
msgstr "فعال‌سازی Multipath TCP، باید در تنظیمات سرور و کلاینت فعال باشد."
msgid "An FinalMaskObject in JSON format, used for sharing."
msgstr "یک شیء FinalMaskObject با فرمت JSON که برای اشتراک‌گذاری استفاده می‌شود."
msgid "Custom finalmask overrides mkcp, hysteria2, fragment, noise, and related settings."
msgstr "ماسک نهایی سفارشی، تنظیمات mkcp، hysteria2، fragment، noise و تنظیمات مرتبط را نادیده می‌گیرد."
msgid "Fragment"
msgstr "قطعه‌بندی (Fragment)"
@ -1698,8 +1719,8 @@ msgstr "طول قطعه"
msgid "Fragmented packet length (byte)"
msgstr "طول بسته قطعه‌بندی شده (بایت)"
msgid "Fragment Interval"
msgstr "بازه قطعه‌بندی"
msgid "Fragment Delay"
msgstr "تأخیر قطعه"
msgid "Fragmentation interval (ms)"
msgstr "بازه قطعه‌بندی (میلی‌ثانیه)"
@ -1708,7 +1729,7 @@ msgid "Max Split"
msgstr "حداکثر تقسیم"
msgid "Limit the maximum number of splits."
msgstr "محدود کردن حداکثر تعداد تقسیم‌ها."
msgstr "حداکثر تعداد تقسیم‌بندی‌ها را محدود کنید."
msgid "Split handshake data into multiple TLS records for better censorship evasion. Low overhead. Recommended to enable first."
msgstr "تقسیم داده‌های دست تکان دادن به چندین رکورد TLS برای فرار بهتر از سانسور. سربار کم. توصیه می‌شود ابتدا فعال شود."
@ -1728,15 +1749,12 @@ msgstr "برای ارسال بسته‌های نویز، \"Noise\" را در ت
msgid "Xray Noise Packets"
msgstr "بسته‌های نویز Xray"
msgid "Packet"
msgstr "بسته"
msgid "Packet | Rand Length"
msgstr "طول بسته | رند"
msgid "Delay (ms)"
msgstr "تاخیر (میلی‌ثانیه)"
msgid "IP Type"
msgstr "نوع IP"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "اگر نام دامنه است، نام دامنه درخواست شده قبل از اتصال به IP حل می‌شود."
@ -2275,6 +2293,9 @@ msgstr "گزینه‌های افزونه ناقص است."
msgid "shadowsocks-libev unsupport 2022 encryption."
msgstr "shadowsocks-libev از رمزنگاری 2022 پشتیبانی نمی‌کند."
msgid "Please replace %s to support %s transmission method."
msgstr "لطفا برای پشتیبانی از روش انتقال %s، %s را جایگزین کنید."
msgid "Please replace Xray or Sing-Box to support more transmission methods in Shadowsocks."
msgstr "لطفاً برای پشتیبانی از روش‌های انتقال بیشتر در Shadowsocks، از Xray یا Sing-Box استفاده کنید."
@ -2361,3 +2382,6 @@ msgstr "خطا، بازیابی سرویس."
msgid "Subscription complete..."
msgstr "اشتراک کامل شد..."
msgid "Processing, please wait…"
msgstr "در حال پردازش، لطفا صبر کنید…"

View File

@ -568,6 +568,9 @@ msgstr "Тип аутентификации"
msgid "Auth Password"
msgstr "Пароль аутентификации"
msgid "Client BBR Flow Control"
msgstr "Управление клиентом BBR"
msgid "Commands the client to use the BBR flow control algorithm"
msgstr "Включает использование алгоритма управления потоком BBR на клиенте"
@ -592,6 +595,9 @@ msgstr "Размер окна приёма соединения QUIC"
msgid "QUIC concurrent bidirectional streams"
msgstr "Макс. количество одновременных двунаправленных потоков QUIC"
msgid "QUIC KeepAlive interval"
msgstr "Интервал QUIC KeepAlive"
msgid "Disable MTU detection"
msgstr "Отключить определение MTU"
@ -1336,6 +1342,9 @@ msgstr "Пропускать проверку сертификата"
msgid "Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."
msgstr "Разрешить соединения без проверки сертификата. При включении валидация будет пропущена."
msgid "Used when the node link does not include this parameter."
msgstr "Используется, если в ссылке на узел этот параметр отсутствует."
msgid "%s Node Use Type"
msgstr "Тип использования сервера %s"
@ -1354,6 +1363,9 @@ msgstr "Пароль для локального SOCKS TUIC"
msgid "UDP relay mode"
msgstr "Режим ретрансляции UDP"
msgid "Concurrent Tunnels"
msgstr "Одновременные туннели"
msgid "Congestion control algorithm"
msgstr "Алгоритм контроля перегрузок"
@ -1675,12 +1687,21 @@ msgstr "Политика запросов ECH"
msgid "Controls the policy used when performing DNS queries for ECH configuration."
msgstr "Управляет политикой DNS-запросов для получения конфигурации ECH."
msgid "ECH Query Domain"
msgstr "Домен запроса ECH"
msgid "Overrides the domain name used for ECH HTTPS record queries."
msgstr "Переопределяет доменное имя, используемое для запросов записей ECH HTTPS."
msgid "Enable Multipath TCP, need to be enabled in both server and client configuration."
msgstr "Включить Multipath TCP. Должно быть активировано и на сервере, и на клиенте."
msgid "An FinalMaskObject in JSON format, used for sharing."
msgstr "Объект FinalMaskObject в формате JSON, используемый для обмена данными."
msgid "Custom finalmask overrides mkcp, hysteria2, fragment, noise, and related settings."
msgstr "Пользовательская настройка finalmask переопределяет параметры mkcp, hysteria2, fragment, noise и связанные с ними настройки."
msgid "Fragment"
msgstr "Фрагментация"
@ -1699,17 +1720,17 @@ msgstr "Длина фрагмента"
msgid "Fragmented packet length (byte)"
msgstr "Длина фрагментированного пакета (байт)"
msgid "Fragment Interval"
msgstr "Интервал фрагментации"
msgid "Fragment Delay"
msgstr "Задержка фрагмента"
msgid "Fragmentation interval (ms)"
msgstr "Интервал между фрагментами (мс)"
msgid "Max Split"
msgstr "Макс. число фрагментов"
msgstr "Максимальное разделение"
msgid "Limit the maximum number of splits."
msgstr "Ограничить максимальное количество фрагментов."
msgstr "Ограничьте максимальное количество разделений."
msgid "Split handshake data into multiple TLS records for better censorship evasion. Low overhead. Recommended to enable first."
msgstr "Разбивать данные рукопожатия на несколько TLS-записей для лучшего обхода цензуры. Малая нагрузка. Рекомендуется включить в первую очередь."
@ -1729,15 +1750,12 @@ msgstr "Чтобы отправлять шумовые пакеты, включ
msgid "Xray Noise Packets"
msgstr "Пакеты шума Xray"
msgid "Packet"
msgstr "Пакет"
msgid "Packet | Rand Length"
msgstr "Пакет | Длина ранда"
msgid "Delay (ms)"
msgstr "Задержка (мс)"
msgid "IP Type"
msgstr "Тип IP"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "Если указано доменное имя, оно будет разрешено в IP-адрес перед подключением."
@ -2273,6 +2291,9 @@ msgstr "Опции плагина указаны не полностью."
msgid "shadowsocks-libev unsupport 2022 encryption."
msgstr "shadowsocks-libev не поддерживает шифрование 2022."
msgid "Please replace %s to support %s transmission method."
msgstr "Пожалуйста, замените %s, чтобы поддержать метод передачи %s."
msgid "Please replace Xray or Sing-Box to support more transmission methods in Shadowsocks."
msgstr "Пожалуйста, используйте Xray или Sing-Box для поддержки дополнительных методов передачи в Shadowsocks."
@ -2359,3 +2380,6 @@ msgstr "Ошибка, выполняется восстановление слу
msgid "Subscription complete..."
msgstr "Подписка завершена..."
msgid "Processing, please wait…"
msgstr "Обработка, пожалуйста, подождите…"

View File

@ -559,6 +559,9 @@ msgstr "认证类型"
msgid "Auth Password"
msgstr "认证密码"
msgid "Client BBR Flow Control"
msgstr "客户端 BBR 流控"
msgid "Commands the client to use the BBR flow control algorithm"
msgstr "命令客户端使用 BBR 流量控制算法"
@ -583,6 +586,9 @@ msgstr "QUIC 连接接收窗口"
msgid "QUIC concurrent bidirectional streams"
msgstr "QUIC 并发双向流的最大数量"
msgid "QUIC KeepAlive interval"
msgstr "QUIC KeepAlive 间隔"
msgid "Disable MTU detection"
msgstr "禁用 MTU 检测"
@ -1327,6 +1333,9 @@ msgstr "允许不安全连接"
msgid "Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."
msgstr "是否允许不安全连接。当勾选时,将跳过证书验证。"
msgid "Used when the node link does not include this parameter."
msgstr "当节点链接未包含该参数时,将使用此设置。"
msgid "%s Node Use Type"
msgstr "%s 节点使用类型"
@ -1345,6 +1354,9 @@ msgstr "用于本地Socks服务器连接的密码"
msgid "UDP relay mode"
msgstr "UDP中继模式"
msgid "Concurrent Tunnels"
msgstr "并发隧道连接数"
msgid "Congestion control algorithm"
msgstr "拥塞控制算法"
@ -1666,12 +1678,21 @@ msgstr "ECH 查询策略"
msgid "Controls the policy used when performing DNS queries for ECH configuration."
msgstr "控制使用 DNS 查询 ECH 配置时的策略。"
msgid "ECH Query Domain"
msgstr "ECH 查询域名"
msgid "Overrides the domain name used for ECH HTTPS record queries."
msgstr "覆盖用于 ECH HTTPS 记录查询的域名。"
msgid "Enable Multipath TCP, need to be enabled in both server and client configuration."
msgstr "启用 Multipath TCP需在服务端和客户端配置中同时启用。"
msgid "An FinalMaskObject in JSON format, used for sharing."
msgstr "JSON 格式的 FinalMaskObject用来实现分享。"
msgid "Custom finalmask overrides mkcp, hysteria2, fragment, noise, and related settings."
msgstr "自定义 finalmask 将覆盖 mkcp、hysteria2、fragment、noise 等相关配置。"
msgid "Fragment"
msgstr "分片"
@ -1690,8 +1711,8 @@ msgstr "分片包长"
msgid "Fragmented packet length (byte)"
msgstr "分片包长 (byte)"
msgid "Fragment Interval"
msgstr "分片间隔"
msgid "Fragment Delay"
msgstr "分片延迟"
msgid "Fragmentation interval (ms)"
msgstr "分片间隔ms"
@ -1720,15 +1741,12 @@ msgstr "在 Xray 设置中勾选 “噪声” 以发送噪声包。"
msgid "Xray Noise Packets"
msgstr "Xray 噪声数据包"
msgid "Packet"
msgstr "数据包"
msgid "Packet | Rand Length"
msgstr "数据包 | Rand 长度"
msgid "Delay (ms)"
msgstr "延迟ms"
msgid "IP Type"
msgstr "IP 类型"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "如果是域名,域名将在请求发出之前解析为 IP。"
@ -2267,6 +2285,9 @@ msgstr "插件选项不完整。"
msgid "shadowsocks-libev unsupport 2022 encryption."
msgstr "shadowsocks-libev 不支持2022加密。"
msgid "Please replace %s to support %s transmission method."
msgstr "请更换 %s 以支持 %s 传输方式。"
msgid "Please replace Xray or Sing-Box to support more transmission methods in Shadowsocks."
msgstr "请更换 Xray 或 Sing-Box 来支持 Shadowsocks 更多的传输方式。"
@ -2353,3 +2374,6 @@ msgstr "发生错误, 正在恢复服务。"
msgid "Subscription complete..."
msgstr "订阅完毕..."
msgid "Processing, please wait…"
msgstr "正在处理,请稍候。"

View File

@ -559,6 +559,9 @@ msgstr "認證類型"
msgid "Auth Password"
msgstr "認證密碼"
msgid "Client BBR Flow Control"
msgstr "客戶端 BBR 流控"
msgid "Commands the client to use the BBR flow control algorithm"
msgstr "命令客戶端使用 BBR 流量控制算法"
@ -583,6 +586,9 @@ msgstr "QUIC 連接接收窗口"
msgid "QUIC concurrent bidirectional streams"
msgstr "QUIC 並發双向流的最大數量"
msgid "QUIC KeepAlive interval"
msgstr "QUIC KeepAlive 間隔"
msgid "Disable MTU detection"
msgstr "禁用 MTU 檢測"
@ -1327,6 +1333,9 @@ msgstr "允許不安全連接"
msgid "Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."
msgstr "是否允許不安全連接。當勾選時,將跳過證書驗證。"
msgid "Used when the node link does not include this parameter."
msgstr "當節點連結未包含該參數時,將使用此設定。"
msgid "%s Node Use Type"
msgstr "%s 節點使用類型"
@ -1345,6 +1354,9 @@ msgstr "用於本地Socks服務器連接的密碼"
msgid "UDP relay mode"
msgstr "UDP中继模式"
msgid "Concurrent Tunnels"
msgstr "並發隧道連接數"
msgid "Congestion control algorithm"
msgstr "拥塞控制算法"
@ -1666,12 +1678,21 @@ msgstr "ECH 查詢策略"
msgid "Controls the policy used when performing DNS queries for ECH configuration."
msgstr "控制使用 DNS 查詢 ECH 配置時的策略。"
msgid "ECH Query Domain"
msgstr "ECH 查詢網域名"
msgid "Overrides the domain name used for ECH HTTPS record queries."
msgstr "覆蓋用於 ECH HTTPS 記錄查詢的網域名。"
msgid "Enable Multipath TCP, need to be enabled in both server and client configuration."
msgstr "啟用 Multipath TCP需在服務端和客戶端配置中同時啟用。"
msgid "An FinalMaskObject in JSON format, used for sharing."
msgstr "JSON 格式的 FinalMaskObject用來實現分享。"
msgid "Custom finalmask overrides mkcp, hysteria2, fragment, noise, and related settings."
msgstr "自定義 finalmask 將涵蓋 mkcp、hysteria2、fragment、noise 等相關配置。"
msgid "Fragment"
msgstr "分片"
@ -1690,8 +1711,8 @@ msgstr "分片包长"
msgid "Fragmented packet length (byte)"
msgstr "分片包长 (byte)"
msgid "Fragment Interval"
msgstr "分片間隔"
msgid "Fragment Delay"
msgstr "分片延遲"
msgid "Fragmentation interval (ms)"
msgstr "分片間隔ms"
@ -1720,15 +1741,12 @@ msgstr "在 Xray 設置中勾選 “噪聲” 以發送噪聲包。"
msgid "Xray Noise Packets"
msgstr "Xray 噪聲數据包"
msgid "Packet"
msgstr "數据包"
msgid "Packet | Rand Length"
msgstr "數據包 | Rand 長度"
msgid "Delay (ms)"
msgstr "延迟ms"
msgid "IP Type"
msgstr "IP 類型"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "如果是域名,域名將在請求發出之前解析為 IP。"
@ -2267,8 +2285,11 @@ msgstr "插件選項不完整。"
msgid "shadowsocks-libev unsupport 2022 encryption."
msgstr "shadowsocks-libev 不支持2022加密。"
msgid "Please replace %s to support %s transmission method."
msgstr "請更換 %s 以支援 %s 傳輸方式。"
msgid "Please replace Xray or Sing-Box to support more transmission methods in Shadowsocks."
msgstr "請更換 Xray 或 Sing-Box 來支持 Shadowsocks 更多的傳輸方式。"
msgstr "請更換 Xray 或 Sing-Box 來支 Shadowsocks 更多的傳輸方式。"
msgid "unsupport %s plugin."
msgstr "不支持 %s 插件。"
@ -2353,3 +2374,6 @@ msgstr "發生錯誤, 正在恢復服務。"
msgid "Subscription complete..."
msgstr "訂閱完毕..."
msgid "Processing, please wait…"
msgstr "正在處理,請稍候。"

View File

@ -13,6 +13,8 @@ IPSET_WAN6="passwall2_wan6"
IPSET_LAN6="passwall2_lan6"
IPSET_VPS6="passwall2_vps6"
FWMARK="0x50535732"
ipt=$(command -v iptables-legacy || command -v iptables)
ip6t=$(command -v ip6tables-legacy || command -v ip6tables)
@ -150,10 +152,9 @@ REDIRECT() {
local s="-j REDIRECT"
[ -n "$1" ] && {
local s="$s --to-ports $1"
[ "$2" == "MARK" ] && s="-j MARK --set-mark $1"
[ "$2" == "TPROXY" ] && {
local mark="-m mark --mark 1"
s="${mark} -j TPROXY --tproxy-mark 0x1/0x1 --on-port $1"
local mark="-m mark --mark ${FWMARK}"
s="${mark} -j TPROXY --on-port $1"
}
}
echo $s
@ -688,7 +689,7 @@ add_firewall_rule() {
$ipt_n -N PSW2_OUTPUT
$ipt_n -A PSW2_OUTPUT $(dst $IPSET_LAN) -j RETURN
$ipt_n -A PSW2_OUTPUT $(dst $IPSET_VPS) -j RETURN
$ipt_n -A PSW2_OUTPUT -m mark --mark 0xff -j RETURN
$ipt_n -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
$ipt_n -N PSW2_DNS
if [ $(config_t_get global dns_redirect "1") = "0" ]; then
@ -697,12 +698,16 @@ add_firewall_rule() {
else
$ipt_n -I PREROUTING -j PSW2_DNS
fi
$ipt_m -N PSW2_DIVERT
$ipt_m -A PSW2_DIVERT -j MARK --set-mark ${FWMARK}
$ipt_m -A PSW2_DIVERT -j ACCEPT
$ipt_m -N PSW2_RULE
$ipt_m -A PSW2_RULE -j CONNMARK --restore-mark
$ipt_m -A PSW2_RULE -m mark --mark 0x1 -j RETURN
$ipt_m -A PSW2_RULE -p tcp -m tcp --syn -j MARK --set-xmark 1
$ipt_m -A PSW2_RULE -p udp -m conntrack --ctstate NEW,RELATED -j MARK --set-xmark 1
$ipt_m -A PSW2_RULE -m mark --mark ${FWMARK} -j RETURN
$ipt_m -A PSW2_RULE -p tcp -m tcp --syn -j MARK --set-xmark ${FWMARK}
$ipt_m -A PSW2_RULE -p udp -m conntrack --ctstate NEW,RELATED -j MARK --set-xmark ${FWMARK}
$ipt_m -A PSW2_RULE -j CONNMARK --save-mark
$ipt_m -N PSW2
@ -722,6 +727,8 @@ add_firewall_rule() {
unset WAN_IP wan_ip
insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW2"
insert_rule_before "$ipt_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT"
insert_rule_before "$ipt_m" "PREROUTING" "PSW2" "-p udp -m socket -j PSW2_DIVERT"
$ipt_m -N PSW2_OUTPUT
$ipt_m -A PSW2_OUTPUT $(dst $IPSET_LAN) -j RETURN
@ -735,10 +742,10 @@ add_firewall_rule() {
log_i18n 1 "$(i18n "Add direct DNS to %s: %s" "iptables" "${dns_address}:${dns_port:-53}")"
done
}
$ipt_m -A PSW2_OUTPUT -m mark --mark 0xff -j RETURN
$ipt_m -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
ip rule add fwmark ${FWMARK} lookup 999 priority 999
ip route add local 0.0.0.0/0 dev lo table 999
[ "$accept_icmpv6" = "1" ] && {
$ip6t_n -N PSW2
@ -749,7 +756,7 @@ add_firewall_rule() {
$ip6t_n -N PSW2_OUTPUT
$ip6t_n -A PSW2_OUTPUT $(dst $IPSET_LAN6) -j RETURN
$ip6t_n -A PSW2_OUTPUT $(dst $IPSET_VPS6) -j RETURN
$ip6t_n -A PSW2_OUTPUT -m mark --mark 0xff -j RETURN
$ip6t_n -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
}
$ip6t_n -N PSW2_DNS
@ -759,12 +766,16 @@ add_firewall_rule() {
else
$ip6t_n -I PREROUTING -j PSW2_DNS
fi
$ip6t_m -N PSW2_DIVERT
$ip6t_m -A PSW2_DIVERT -j MARK --set-mark ${FWMARK}
$ip6t_m -A PSW2_DIVERT -j ACCEPT
$ip6t_m -N PSW2_RULE
$ip6t_m -A PSW2_RULE -j CONNMARK --restore-mark
$ip6t_m -A PSW2_RULE -m mark --mark 0x1 -j RETURN
$ip6t_m -A PSW2_RULE -p tcp -m tcp --syn -j MARK --set-xmark 1
$ip6t_m -A PSW2_RULE -p udp -m conntrack --ctstate NEW,RELATED -j MARK --set-xmark 1
$ip6t_m -A PSW2_RULE -m mark --mark ${FWMARK} -j RETURN
$ip6t_m -A PSW2_RULE -p tcp -m tcp --syn -j MARK --set-xmark ${FWMARK}
$ip6t_m -A PSW2_RULE -p udp -m conntrack --ctstate NEW,RELATED -j MARK --set-xmark ${FWMARK}
$ip6t_m -A PSW2_RULE -j CONNMARK --save-mark
$ip6t_m -N PSW2
@ -783,15 +794,17 @@ add_firewall_rule() {
unset WAN6_IP wan6_ip
insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW2"
insert_rule_before "$ip6t_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT"
insert_rule_before "$ip6t_m" "PREROUTING" "PSW2" "-p udp -m socket -j PSW2_DIVERT"
$ip6t_m -N PSW2_OUTPUT
$ip6t_m -A PSW2_OUTPUT -m mark --mark 0xff -j RETURN
$ip6t_m -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
$ip6t_m -A PSW2_OUTPUT $(dst $IPSET_LAN6) -j RETURN
$ip6t_m -A PSW2_OUTPUT $(dst $IPSET_VPS6) -j RETURN
$ip6t_m -A PSW2_OUTPUT -m conntrack --ctdir REPLY -j RETURN
ip -6 rule add fwmark 1 table 100
ip -6 route add local ::/0 dev lo table 100
ip -6 rule add fwmark ${FWMARK} table 999 priority 999
ip -6 route add local ::/0 dev lo table 999
[ "$ENABLED_DEFAULT_ACL" == 1 ] && {
local ipt_tmp=$ipt_n
@ -910,10 +923,10 @@ add_firewall_rule() {
fi
$ipt_m -I OUTPUT $(comment "mangle-OUTPUT-PSW2") -o lo -j RETURN
insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -m mark --mark 1 -j RETURN"
insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -m mark --mark ${FWMARK} -j RETURN"
$ip6t_m -I OUTPUT $(comment "mangle-OUTPUT-PSW2") -o lo -j RETURN
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -m mark --mark 1 -j RETURN"
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -m mark --mark ${FWMARK} -j RETURN"
$ipt_m -A PSW2 -p udp --dport 53 -j RETURN
$ip6t_m -A PSW2 -p udp --dport 53 -j RETURN
@ -934,17 +947,17 @@ del_firewall_rule() {
$ipt -D $chain $index 2>/dev/null
done
done
for chain in "PSW2" "PSW2_OUTPUT" "PSW2_DNS" "PSW2_RULE"; do
for chain in "PSW2" "PSW2_OUTPUT" "PSW2_DIVERT" "PSW2_DNS" "PSW2_RULE"; do
$ipt -F $chain 2>/dev/null
$ipt -X $chain 2>/dev/null
done
done
ip rule del fwmark 1 lookup 100 2>/dev/null
ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
ip rule del fwmark ${FWMARK} lookup 999 priority 999 2>/dev/null
ip route del local 0.0.0.0/0 dev lo table 999 2>/dev/null
ip -6 rule del fwmark 1 table 100 2>/dev/null
ip -6 route del local ::/0 dev lo table 100 2>/dev/null
ip -6 rule del fwmark ${FWMARK} table 999 priority 999 2>/dev/null
ip -6 route del local ::/0 dev lo table 999 2>/dev/null
log_i18n 0 "Delete %s rules is complete." "iptables"
}
@ -968,7 +981,7 @@ gen_include() {
[ -z "${_ipt}" ] && return
echo "*$2"
${_ipt}-save -t $2 | grep "PSW2" | grep -v "\-j PSW2$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
${_ipt}-save -t $2 | grep "PSW2" | grep -v "\-j PSW2$" | grep -v "socket \-j PSW2_DIVERT$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
echo 'COMMIT'
}
local __ipt=""
@ -985,6 +998,8 @@ gen_include() {
[ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW2")
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW2")
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT")
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "PSW2" "-p udp -m socket -j PSW2_DIVERT")
WAN_IP=\$(get_wan_ips ip4)
[ ! -z "\${WAN_IP}" ] && {
@ -1009,6 +1024,8 @@ gen_include() {
[ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING -p ipv6-icmp -j PSW2
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW2")
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT")
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "PSW2" "-p udp -m socket -j PSW2_DIVERT")
WAN6_IP=\$(get_wan_ips ip6)
[ ! -z "\${WAN6_IP}" ] && {

View File

@ -14,6 +14,8 @@ NFTSET_WAN6="passwall2_wan6"
NFTSET_LAN6="passwall2_lan6"
NFTSET_VPS6="passwall2_vps6"
FWMARK="0x50535732"
FWI=$(uci -q get firewall.passwall2.path 2>/dev/null)
FAKE_IP="198.18.0.0/16"
FAKE_IP_6="fc00::/18"
@ -103,15 +105,14 @@ REDIRECT() {
local s="counter redirect"
[ -n "$1" ] && {
local s="$s to :$1"
[ "$2" == "MARK" ] && s="counter meta mark set $1"
[ "$2" == "TPROXY" ] && {
s="counter meta mark 1 tproxy to :$1"
s="counter meta mark ${FWMARK} tproxy to :$1"
}
[ "$2" == "TPROXY4" ] && {
s="counter meta mark 1 tproxy ip to :$1"
s="counter meta mark ${FWMARK} tproxy ip to :$1"
}
[ "$2" == "TPROXY6" ] && {
s="counter meta mark 1 tproxy ip6 to :$1"
s="counter meta mark ${FWMARK} tproxy ip6 to :$1"
}
}
@ -500,7 +501,7 @@ load_acl() {
[ "$UDP_NO_REDIR_PORTS" != "disable" ] && {
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 counter meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"${comment}\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp $(factor $UDP_NO_REDIR_PORTS "udp dport") counter return comment \"${comment}\""
if ! has_1_65535 "$UDP_NO_REDIR_PORTS"; then
log 2 "${msg}$(i18n "not proxy %s port [%s]" "UDP" "${UDP_NO_REDIR_PORTS}")"
else
@ -712,6 +713,11 @@ add_firewall_rule() {
nft_output_chain="PSW2_OUTPUT_MANGLE"
fi
nft "add chain $NFTABLE_NAME PSW2_DIVERT"
nft "flush chain $NFTABLE_NAME PSW2_DIVERT"
nft "add rule $NFTABLE_NAME PSW2_DIVERT meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept"
nft "add rule $NFTABLE_NAME PSW2_DIVERT meta l4proto udp socket transparent 1 mark set ${FWMARK} counter accept"
nft "add chain $NFTABLE_NAME PSW2_DNS"
nft "flush chain $NFTABLE_NAME PSW2_DNS"
if [ $(config_t_get global dns_redirect "1") = "0" ]; then
@ -725,11 +731,11 @@ add_firewall_rule() {
# for ipv4 ipv6 tproxy mark
nft "add chain $NFTABLE_NAME PSW2_RULE"
nft "flush chain $NFTABLE_NAME PSW2_RULE"
nft "add rule $NFTABLE_NAME PSW2_RULE meta mark set ct mark counter"
nft "add rule $NFTABLE_NAME PSW2_RULE meta mark 1 counter return"
nft "add rule $NFTABLE_NAME PSW2_RULE tcp flags syn meta mark set mark and 0x0 xor 0x1 counter"
nft "add rule $NFTABLE_NAME PSW2_RULE meta l4proto udp ct state new,related meta mark set mark and 0x0 xor 0x1 counter"
nft "add rule $NFTABLE_NAME PSW2_RULE ct mark set mark counter"
nft "add rule $NFTABLE_NAME PSW2_RULE counter meta mark set ct mark"
nft "add rule $NFTABLE_NAME PSW2_RULE meta mark ${FWMARK} counter return"
nft "add rule $NFTABLE_NAME PSW2_RULE tcp flags & (fin|syn|rst|ack) == syn counter meta mark set ${FWMARK}"
nft "add rule $NFTABLE_NAME PSW2_RULE meta l4proto udp ct state new,related counter meta mark set ${FWMARK}"
nft "add rule $NFTABLE_NAME PSW2_RULE counter ct mark set mark"
#ipv4 tproxy mode and udp
nft "add chain $NFTABLE_NAME PSW2_MANGLE"
@ -751,9 +757,10 @@ add_firewall_rule() {
log_i18n 1 "$(i18n "Add direct DNS to %s: %s" "nftables" "${dns_address}:${dns_port:-53}")"
done
}
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE meta mark 0xff counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE meta mark 255 counter return"
# jump chains
nft "add rule $NFTABLE_NAME mangle_prerouting counter jump PSW2_DIVERT"
nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol udp counter jump PSW2_MANGLE"
[ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol tcp counter jump PSW2_MANGLE"
@ -769,7 +776,7 @@ add_firewall_rule() {
nft "flush chain $NFTABLE_NAME PSW2_OUTPUT_NAT"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT ip daddr @$NFTSET_LAN counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT ip daddr @$NFTSET_VPS counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT meta mark 0xff counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT meta mark 255 counter return"
}
#icmp ipv6-icmp redirect
@ -797,8 +804,8 @@ add_firewall_rule() {
}
unset WAN_IP
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
ip rule add fwmark ${FWMARK} lookup 999 priority 999
ip route add local 0.0.0.0/0 dev lo table 999
#ipv6 tproxy mode and udp
nft "add chain $NFTABLE_NAME PSW2_MANGLE_V6"
@ -812,7 +819,7 @@ add_firewall_rule() {
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_LAN6 counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ct direction reply counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta mark 0xff counter return"
nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta mark 255 counter return"
# jump chains
[ "$PROXY_IPV6" == "1" ] && {
@ -827,8 +834,8 @@ add_firewall_rule() {
}
unset WAN6_IP
ip -6 rule add fwmark 1 table 100
ip -6 route add local ::/0 dev lo table 100
ip -6 rule add fwmark ${FWMARK} table 999 priority 999
ip -6 route add local ::/0 dev lo table 999
}
[ "$ENABLED_DEFAULT_ACL" == 1 ] && {
@ -945,7 +952,7 @@ add_firewall_rule() {
fi
nft "add rule $NFTABLE_NAME mangle_output oif lo counter return comment \"PSW2_OUTPUT_MANGLE\""
nft "add rule $NFTABLE_NAME mangle_output meta mark 1 counter return comment \"PSW2_OUTPUT_MANGLE\""
nft "add rule $NFTABLE_NAME mangle_output meta mark ${FWMARK} counter return comment \"PSW2_OUTPUT_MANGLE\""
nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp udp dport 53 counter return"
nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp udp dport 53 counter return"
@ -973,11 +980,11 @@ del_firewall_rule() {
# Need to be removed at the end, otherwise it will show "Resource busy"
nft delete chain $NFTABLE_NAME handle $(nft -a list chains | grep -E "PSW2_RULE" | awk -F '# handle ' '{print$2}') 2>/dev/null
ip rule del fwmark 1 lookup 100 2>/dev/null
ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
ip rule del fwmark ${FWMARK} lookup 999 priority 999 2>/dev/null
ip route del local 0.0.0.0/0 dev lo table 999 2>/dev/null
ip -6 rule del fwmark 1 table 100 2>/dev/null
ip -6 route del local ::/0 dev lo table 100 2>/dev/null
ip -6 rule del fwmark ${FWMARK} table 999 priority 999 2>/dev/null
ip -6 route del local ::/0 dev lo table 999 2>/dev/null
destroy_nftset $NFTSET_LOCAL
destroy_nftset $NFTSET_WAN

View File

@ -453,7 +453,7 @@ local function processData(szType, content, add_mode, group)
end
result.type = "SSR"
local dat = split(content, "/%?")
local dat = split(content:gsub("/%?", "?"), "%?")
local hostInfo = split(dat[1], ':')
if dat[1]:match('%[(.*)%]') then
result.address = dat[1]:match('%[(.*)%]')
@ -467,8 +467,10 @@ local function processData(szType, content, add_mode, group)
result.password = base64Decode(hostInfo[#hostInfo])
local params = {}
for _, v in pairs(split(dat[2], '&')) do
local t = split(v, '=')
params[t[1]] = t[2]
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1)] = v:sub(s + 1)
end
end
result.obfs_param = base64Decode(params.obfsparam)
result.protocol_param = base64Decode(params.protoparam)
@ -587,14 +589,10 @@ local function processData(szType, content, add_mode, group)
if info.tls == "tls" or info.tls == "1" then
result.tls = "1"
result.tls_serverName = (info.sni and info.sni ~= "") and info.sni or info.host
info.allowinsecure = info.allowinsecure or info.insecure
if info.allowinsecure and (info.allowinsecure == "1" or info.allowinsecure == "0") then
result.tls_allowInsecure = info.allowinsecure
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
result.tls_CertSha = info.pcs
result.tls_CertByName = info.vcn
local insecure = info.allowinsecure or info.allowInsecure or info.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (allowInsecure_default and "1" or "0")
else
result.tls = "0"
end
@ -633,8 +631,10 @@ local function processData(szType, content, add_mode, group)
local find_index = info:find("%?")
local query = split(info, "%?")
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
if #t >= 2 then params[t[1]] = UrlDecode(t[2]) end
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1)] = UrlDecode(v:sub(s + 1))
end
end
if params.plugin then
local plugin_info = params.plugin
@ -838,7 +838,7 @@ local function processData(szType, content, add_mode, group)
end
if params.type == 'xhttp' then
if result.type ~= "Xray" then
result.error_msg = "请更换 Xray 以支持 xhttp 传输方式."
result.error_msg = i18n.translatef("Please replace %s to support %s transmission method.", "Xray", "xhttp")
end
result.xhttp_host = params.host
result.xhttp_path = params.path
@ -876,12 +876,8 @@ local function processData(szType, content, add_mode, group)
result.reality_mldsa65Verify = params.pqv or nil
end
end
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure and (params.allowinsecure == "1" or params.allowinsecure == "0") then
result.tls_allowInsecure = params.allowinsecure
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
local insecure = params.allowinsecure or params.allowInsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (allowInsecure_default and "1" or "0")
result.uot = params.udp
elseif (params.type ~= "tcp" and params.type ~= "raw") and (params.headerType and params.headerType ~= "none") then
result.error_msg = i18n.translatef("Please replace Xray or Sing-Box to support more transmission methods in Shadowsocks.")
@ -959,9 +955,9 @@ local function processData(szType, content, add_mode, group)
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
if #t > 1 then
params[string.lower(t[1])] = UrlDecode(t[2])
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1)] = UrlDecode(v:sub(s + 1))
end
end
-- [2001:4860:4860::8888]:443
@ -978,32 +974,14 @@ local function processData(szType, content, add_mode, group)
result.address = host_port
end
local peer, sni = nil, ""
if params.peer then peer = params.peer end
sni = params.sni and params.sni or ""
if params.ws and params.ws == "1" then
result.trojan_transport = "ws"
if params.wshost then result.ws_host = params.wshost end
if params.wspath then result.ws_path = params.wspath end
if sni == "" and params.wshost then sni = params.wshost end
end
result.port = port
result.tls = '1'
result.tls_serverName = peer and peer or sni
result.tls_serverName = params.peer or params.sni or ""
result.tls_CertSha = params.pcs
result.tls_CertByName = params.vcn
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure then
if params.allowinsecure == "1" or params.allowinsecure == "0" then
result.tls_allowInsecure = params.allowinsecure
else
result.tls_allowInsecure = string.lower(params.allowinsecure) == "true" and "1" or "0"
end
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
local insecure = params.allowinsecure or params.allowInsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (allowInsecure_default and "1" or "0")
if not params.type then params.type = "tcp" end
params.type = string.lower(params.type)
@ -1071,8 +1049,7 @@ local function processData(szType, content, add_mode, group)
result.quic_security = params.quicSecurity or "none"
end
if params.type == 'grpc' then
if params.path then result.grpc_serviceName = params.path end
if params.serviceName then result.grpc_serviceName = params.serviceName end
result.grpc_serviceName = params.serviceName or params.path
result.grpc_mode = params.mode or "gun"
end
if params.type == 'xhttp' then
@ -1131,8 +1108,10 @@ local function processData(szType, content, add_mode, group)
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
params[t[1]] = UrlDecode(t[2])
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1)] = UrlDecode(v:sub(s + 1))
end
end
-- [2001:4860:4860::8888]:443
-- 8.8.8.8:443
@ -1265,17 +1244,11 @@ local function processData(szType, content, add_mode, group)
result.use_mldsa65Verify = (params.pqv and params.pqv ~= "") and "1" or nil
result.reality_mldsa65Verify = params.pqv or nil
end
local insecure = params.allowinsecure or params.allowInsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (allowInsecure_default and "1" or "0")
end
result.port = port
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure and (params.allowinsecure == "1" or params.allowinsecure == "0") then
result.tls_allowInsecure = params.allowinsecure
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
result.tcp_fast_open = params.tfo
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
@ -1302,13 +1275,13 @@ local function processData(szType, content, add_mode, group)
end
result.remarks = UrlDecode(alias)
local dat = split(content:gsub("/%?", "?"), '%?')
local host_port = dat[1]
local query = split(content:gsub("/%?", "?"), '%?')
local host_port = query[1]
local params = {}
for _, v in pairs(split(dat[2], '&')) do
local t = split(v, '=')
if #t > 0 then
params[t[1]] = t[2]
for _, v in pairs(split(query[2], '&')) do
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1)] = v:sub(s + 1)
end
end
-- [2001:4860:4860::8888]:443
@ -1327,14 +1300,10 @@ local function processData(szType, content, add_mode, group)
result.hysteria_obfs = params.obfsParam
result.hysteria_auth_type = "string"
result.hysteria_auth_password = params.auth
result.tls_serverName = params.peer
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure and (params.allowinsecure == "1" or params.allowinsecure == "0") then
result.tls_allowInsecure = params.allowinsecure
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
result.hysteria_alpn = params.alpn
result.tls_serverName = params.peer or params.sni or ""
local insecure = params.allowinsecure or params.allowInsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (allowInsecure_default and "1" or "0")
result.alpn = params.alpn
result.hysteria_up_mbps = params.upmbps
result.hysteria_down_mbps = params.downmbps
result.hysteria_hop = params.mport
@ -1357,9 +1326,9 @@ local function processData(szType, content, add_mode, group)
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
if #t > 1 then
params[string.lower(t[1])] = UrlDecode(t[2])
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1):lower()] = UrlDecode(v:sub(s + 1))
end
end
-- [2001:4860:4860::8888]:443
@ -1378,12 +1347,8 @@ local function processData(szType, content, add_mode, group)
result.tls_serverName = params.sni
result.tls_CertSha = params.pcs
result.tls_CertByName = params.vcn
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure and (params.allowinsecure == "1" or params.allowinsecure == "0") then
result.tls_allowInsecure = params.allowinsecure
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
local insecure = params.allowinsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (allowInsecure_default and "1" or "0")
result.hysteria2_tls_pinSHA256 = params.pinSHA256
result.hysteria2_hop = params.mport
@ -1441,9 +1406,9 @@ local function processData(szType, content, add_mode, group)
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
if #t > 1 then
params[string.lower(t[1])] = UrlDecode(t[2])
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1):lower()] = UrlDecode(v:sub(s + 1))
end
end
if host_port:find(":") then
@ -1462,16 +1427,8 @@ local function processData(szType, content, add_mode, group)
result.tuic_alpn = params.alpn or "default"
result.tuic_congestion_control = params.congestion_control or "cubic"
result.tuic_udp_relay_mode = params.udp_relay_mode or "native"
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure then
if params.allowinsecure == "1" or params.allowinsecure == "0" then
result.tls_allowInsecure = params.allowinsecure
else
result.tls_allowInsecure = string.lower(params.allowinsecure) == "true" and "1" or "0"
end
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
local insecure = params.allowinsecure or params.insecure or params.allow_insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (allowInsecure_default and "1" or "0")
elseif szType == "anytls" then
if has_singbox then
result.type = 'sing-box'
@ -1497,8 +1454,10 @@ local function processData(szType, content, add_mode, group)
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local t = split(v, '=')
params[t[1]] = UrlDecode(t[2])
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1):lower()] = UrlDecode(v:sub(s + 1))
end
end
-- [2001:4860:4860::8888]:443
-- 8.8.8.8:443
@ -1514,7 +1473,7 @@ local function processData(szType, content, add_mode, group)
result.address = host_port
end
result.tls = "0"
if (not params.security or params.security == "") and params.sni and params.sni ~= "" then
if not params.security or params.security == "" then
params.security = "tls"
end
if params.security == "tls" or params.security == "reality" then
@ -1532,18 +1491,70 @@ local function processData(szType, content, add_mode, group)
end
end
result.port = port
params.allowinsecure = params.allowinsecure or params.insecure
if params.allowinsecure and (params.allowinsecure == "1" or params.allowinsecure == "0") then
result.tls_allowInsecure = params.allowinsecure
local insecure = params.allowinsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (allowInsecure_default and "1" or "0")
end
elseif szType == 'naive+https' or szType == 'naive+quic' then
if has_singbox then
result.type = 'sing-box'
result.protocol = "naive"
else
log(2, i18n.translatef("Skip the %s node because the %s core program is not installed.", "NaïveProxy", "Sing-Box"))
return nil
end
local alias = ""
if content:find("#") then
local idx_sp = content:find("#")
alias = content:sub(idx_sp + 1, -1)
content = content:sub(0, idx_sp - 1)
end
result.remarks = UrlDecode(alias)
local Info = content
if content:find("@", 1, true) then
local contents = split(content, "@")
local auth = contents[1] or ""
local idx = auth:find(":", 1, true)
if not idx then -- Fixed some links that encode the colon between username and password.
auth = UrlDecode(auth)
idx = auth:find(":", 1, true)
end
if idx then
result.username = UrlDecode(auth:sub(1, idx - 1))
result.password = UrlDecode(auth:sub(idx + 1))
end
Info = (contents[2] or ""):gsub("/%?", "?")
end
local query = split(Info, "%?")
local host_port = query[1]
local params = {}
for _, v in pairs(split(query[2], '&')) do
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1)] = UrlDecode(v:sub(s + 1))
end
end
if host_port:find(":") then
local sp = split(host_port, ":")
result.port = sp[#sp]
if api.is_ipv6addrport(host_port) then
result.address = api.get_ipv6_only(host_port)
else
result.tls_allowInsecure = allowInsecure_default and "1" or "0"
end
local singbox_version = api.get_app_version("sing-box")
local version_ge_1_12 = api.compare_versions(singbox_version:match("[^v]+"), ">=", "1.12.0")
if not has_singbox or not version_ge_1_12 then
log(2, i18n.translatef("Skip the %s node, as %s type nodes require Sing-Box version 1.12 or higher.", result.remarks, szType))
return nil
result.address = sp[1]
end
else
result.address = host_port
end
result.tls_serverName = params.sni
result.uot = params.uot
result.naive_insecure_concurrency = params["insecure-concurrency"] or "0"
if params.ech and params.ech ~= "" then
result.ech = "1"
result.ech_config = params.ech
end
if szType == "naive+quic" then
result.naive_quic = "1"
result.naive_congestion_control = params.congestion_control or "bbr"
end
else
log(2, i18n.translatef("%s type node subscriptions are not currently supported, skip this node.", szType))

View File

@ -48,7 +48,7 @@ eval_set_val() {
eval_unset_val() {
for i in $@; do
for j in $i; do
eval unset j
eval unset $j
done
done
}