update 2024-02-27 02:10:59

This commit is contained in:
github-actions[bot] 2024-02-27 02:10:59 +08:00
parent 5323a5481a
commit fd19967c7c
19 changed files with 431 additions and 172 deletions

View File

@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=4.74-4
PKG_VERSION:=4.75-1
PKG_RELEASE:=
PKG_CONFIG_DEPENDS:= \

View File

@ -54,7 +54,8 @@ for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
nodes_table[#nodes_table + 1] = {
id = e[".name"],
remarks = e["remark"]
remarks = e["remark"],
type = e["type"]
}
end
if e.protocol == "_balancing" then
@ -80,11 +81,13 @@ local o = s:option(ListValue, option_name("balancingStrategy"), translate("Balan
o:depends({ [option_name("protocol")] = "_balancing" })
o:value("random")
o:value("leastPing")
o.default = "random"
o:value("leastLoad")
o.default = "leastLoad"
-- 探测地址
local o = s:option(Flag, option_name("useCustomProbeUrl"), translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
o:depends({ [option_name("balancingStrategy")] = "leastPing" })
o:depends({ [option_name("balancingStrategy")] = "leastLoad" })
local o = s:option(Value, option_name("probeUrl"), translate("Probe URL"))
o:depends({ [option_name("useCustomProbeUrl")] = true })
@ -94,6 +97,7 @@ o.description = translate("The URL used to detect the connection status.")
-- 探测间隔
local o = s:option(Value, option_name("probeInterval"), translate("Probe Interval"))
o:depends({ [option_name("balancingStrategy")] = "leastPing" })
o:depends({ [option_name("balancingStrategy")] = "leastLoad" })
o.default = "1m"
o.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
@ -522,4 +526,21 @@ o.default = 0
o = s:option(Flag, option_name("tcpNoDelay"), "tcpNoDelay")
o.default = 0
o = s:option(ListValue, option_name("to_node"), translate("Landing node"), translate("Only support a layer of proxy."))
o.default = ""
o:value("", translate("Close(Not use)"))
for k, v in pairs(nodes_table) do
if v.type == "Xray" then
o:value(v.id, v.remarks)
end
end
for i, v in ipairs(s.fields[option_name("protocol")].keylist) do
if not v:find("_") then
s.fields[option_name("tcpMptcp")]:depends({ [option_name("protocol")] = v })
s.fields[option_name("tcpNoDelay")]:depends({ [option_name("protocol")] = v })
s.fields[option_name("to_node")]:depends({ [option_name("protocol")] = v })
end
end
api.luci_types(arg[1], m, s, type_name, option_prefix)

View File

@ -248,6 +248,11 @@ function gen_outbound(flag, node, tag, proxy_table)
reserved = (node.protocol == "wireguard" and node.wireguard_reserved) and node.wireguard_reserved or nil
}
}
if node.protocol == "wireguard" then
result.settings.kernelMode = false
end
local alpn = {}
if node.alpn and node.alpn ~= "default" then
string.gsub(node.alpn, '[^' .. "," .. ']+', function(w)
@ -665,7 +670,7 @@ function gen_config(var)
selector = valid_nodes,
strategy = { type = _node.balancingStrategy or "random" }
}
if _node.balancingStrategy == "leastPing" then
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" then
if not observatory then
observatory = {
subjectSelector = { "blc-" },
@ -692,6 +697,34 @@ function gen_config(var)
return balancer, rule
end
local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
if not node or not outbound or not outbounds_table then return nil end
local default_outTag = outbound.tag
if node.to_node then
local to_node = uci:get_all(appname, node.to_node)
if to_node then
local to_outbound = gen_outbound(nil, to_node)
if to_outbound then
if shunt_rule_name then
to_outbound.tag = outbound.tag
outbound.tag = node[".name"]
else
to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag
end
to_outbound.proxySettings = {
tag = outbound.tag,
transportLayer = true
}
table.insert(outbounds_table, to_outbound)
default_outTag = to_outbound.tag
end
end
end
return default_outTag
end
if node.protocol == "_shunt" then
local rules = {}
local balancers = {}
@ -723,6 +756,7 @@ function gen_config(var)
elseif preproxy_node and api.is_normal_node(preproxy_node) then
local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag, { fragment = xray_settings.fragment == "1" or nil })
if preproxy_outbound then
set_outbound_detour(preproxy_node, preproxy_outbound, outbounds, preproxy_tag)
table.insert(outbounds, preproxy_outbound)
else
preproxy_enabled = false
@ -738,7 +772,7 @@ function gen_config(var)
end
end
local function gen_shunt_node(rule_name, _node_id, as_proxy)
local function gen_shunt_node(rule_name, _node_id)
if not rule_name then return nil, nil end
if not _node_id then _node_id = node[rule_name] or "nil" end
local rule_outboundTag
@ -832,6 +866,7 @@ function gen_config(var)
end
local _outbound = gen_outbound(flag, _node, rule_name, proxy_table)
if _outbound then
set_outbound_detour(_node, _outbound, outbounds, rule_name)
table.insert(outbounds, _outbound)
if proxy then preproxy_used = true end
rule_outboundTag = rule_name
@ -984,26 +1019,27 @@ function gen_config(var)
}
}
end
else
local outbound = nil
if node.protocol == "_iface" then
if node.iface then
outbound = {
protocol = "freedom",
tag = "outbound",
streamSettings = {
sockopt = {
mark = 255,
interface = node.iface
}
elseif node.protocol == "_iface" then
if node.iface then
local outbound = {
protocol = "freedom",
tag = "outbound",
streamSettings = {
sockopt = {
mark = 255,
interface = node.iface
}
}
sys.call("touch /tmp/etc/passwall/iface/" .. node.iface)
end
else
outbound = gen_outbound(flag, node, nil, { fragment = xray_settings.fragment == "1" or nil })
}
table.insert(outbounds, outbound)
sys.call("touch /tmp/etc/passwall/iface/" .. node.iface)
end
else
local outbound = gen_outbound(flag, node, nil, { fragment = xray_settings.fragment == "1" or nil })
if outbound then
set_outbound_detour(node, outbound, outbounds)
table.insert(outbounds, outbound)
end
if outbound then table.insert(outbounds, outbound) end
routing = {
domainStrategy = "AsIs",
domainMatcher = "hybrid",

View File

@ -225,6 +225,10 @@ local api = require "luci.passwall.api"
if (v_transport === "ws") {
info.host = opt.get(dom_prefix + "ws_host").value;
info.path = opt.get(dom_prefix + "ws_path").value;
if (v_type == "sing-box" && opt.get(dom_prefix + "ws_enableEarlyData").checked) {
var ws_maxEarlyData = opt.get(dom_prefix + "ws_maxEarlyData").value;
info.path = info.path + "?ed=" + ws_maxEarlyData;
}
} else if (v_transport === "h2") {
info.host = opt.get(dom_prefix + "h2_host").value;
info.path = opt.get(dom_prefix + "h2_path").value;
@ -269,6 +273,10 @@ local api = require "luci.passwall.api"
if (v_transport === "ws") {
params += opt.query("host", dom_prefix + "ws_host");
params += opt.query("path", dom_prefix + "ws_path");
if (v_type == "sing-box" && opt.get(dom_prefix + "ws_enableEarlyData").checked) {
var ws_maxEarlyData = opt.get(dom_prefix + "ws_maxEarlyData").value;
params += "?ed=" + ws_maxEarlyData;
}
} else if (v_transport === "h2") {
v_transport = "http";
params += opt.query("host", dom_prefix + "h2_host");
@ -704,6 +712,24 @@ local api = require "luci.passwall.api"
} else if (ssm.net === "ws") {
opt.set(dom_prefix + 'ws_host', ssm.host);
opt.set(dom_prefix + 'ws_path', ssm.path);
if (dom_prefix == "singbox_" && ssm.path && ssm.path.length > 1) {
var ws_path_params = {};
var ws_path_dat = ssm.path.split('?');
var ws_path = ws_path_dat[0];
var ws_path_params = {};
var ws_path_params_array = ws_path_dat[1].split('&');
for (i = 0; i < ws_path_params_array.length; i++) {
var kv = ws_path_params_array[i].split('=');
ws_path_params[decodeURIComponent(kv[0]).toLowerCase()] = decodeURIComponent(kv[1] || '');
}
if (ws_path_params.ed) {
opt.set(dom_prefix + 'ws_path', ws_path);
opt.set(dom_prefix + 'ws_enableEarlyData', true);
opt.set(dom_prefix + 'ws_maxEarlyData', ws_path_params.ed);
opt.set(dom_prefix + 'ws_earlyDataHeaderName', 'Sec-WebSocket-Protocol');
}
}
} else if (ssm.net === "h2") {
opt.set(dom_prefix + 'h2_host', ssm.host);
opt.set(dom_prefix + 'h2_path', ssm.path);
@ -793,6 +819,24 @@ local api = require "luci.passwall.api"
} else if (queryParam.type === "ws") {
opt.set(dom_prefix + 'ws_host', queryParam.host || "");
opt.set(dom_prefix + 'ws_path', queryParam.path || "");
if (dom_prefix == "singbox_" && queryParam.path && queryParam.path.length > 1) {
var ws_path_params = {};
var ws_path_dat = queryParam.path.split('?');
var ws_path = ws_path_dat[0];
var ws_path_params = {};
var ws_path_params_array = ws_path_dat[1].split('&');
for (i = 0; i < ws_path_params_array.length; i++) {
var kv = ws_path_params_array[i].split('=');
ws_path_params[decodeURIComponent(kv[0]).toLowerCase()] = decodeURIComponent(kv[1] || '');
}
if (ws_path_params.ed) {
opt.set(dom_prefix + 'ws_path', ws_path);
opt.set(dom_prefix + 'ws_enableEarlyData', true);
opt.set(dom_prefix + 'ws_maxEarlyData', ws_path_params.ed);
opt.set(dom_prefix + 'ws_earlyDataHeaderName', 'Sec-WebSocket-Protocol');
}
}
} else if (queryParam.type === "h2" || queryParam.type === "http") {
opt.set(dom_prefix + 'h2_host', queryParam.host || "");
opt.set(dom_prefix + 'h2_path', queryParam.path || "");

View File

@ -7,6 +7,8 @@
8.8.8.8
208.67.222.222
208.67.220.220
104.16.249.249
104.16.248.249
1.1.1.1
1.1.1.2
1.0.0.1
@ -16,4 +18,4 @@
2001:b28:f23c::/48
2001:b28:f23d::/48
2001:b28:f23f::/48
2001:b28:f242::/48
2001:b28:f242::/48

View File

@ -406,6 +406,21 @@ local function processData(szType, content, add_mode, add_from)
if info.net == 'ws' then
result.ws_host = info.host
result.ws_path = info.path
if result.type == "sing-box" and info.path then
local ws_path_dat = split(info.path, "?")
local ws_path = ws_path_dat[1]
local ws_path_params = {}
for _, v in pairs(split(ws_path_dat[2], '&')) do
local t = split(v, '=')
ws_path_params[t[1]] = t[2]
end
if ws_path_params.ed and tonumber(ws_path_params.ed) then
result.ws_path = ws_path
result.ws_enableEarlyData = "1"
result.ws_maxEarlyData = tonumber(ws_path_params.ed)
result.ws_earlyDataHeaderName = "Sec-WebSocket-Protocol"
end
end
end
if info.net == 'h2' then
result.h2_host = info.host
@ -693,6 +708,21 @@ local function processData(szType, content, add_mode, add_from)
if params.type == 'ws' then
result.ws_host = params.host
result.ws_path = params.path
if result.type == "sing-box" and params.path then
local ws_path_dat = split(params.path, "?")
local ws_path = ws_path_dat[1]
local ws_path_params = {}
for _, v in pairs(split(ws_path_dat[2], '&')) do
local t = split(v, '=')
ws_path_params[t[1]] = t[2]
end
if ws_path_params.ed and tonumber(ws_path_params.ed) then
result.ws_path = ws_path
result.ws_enableEarlyData = "1"
result.ws_maxEarlyData = tonumber(ws_path_params.ed)
result.ws_earlyDataHeaderName = "Sec-WebSocket-Protocol"
end
end
end
if params.type == 'h2' or params.type == 'http' then
params.type = "h2"

View File

@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=1.26-3
PKG_VERSION:=1.27-1
PKG_RELEASE:=
PKG_CONFIG_DEPENDS:= \

View File

@ -54,7 +54,8 @@ for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
nodes_table[#nodes_table + 1] = {
id = e[".name"],
remarks = e["remark"]
remarks = e["remark"],
type = e["type"]
}
end
if e.protocol == "_balancing" then
@ -80,11 +81,13 @@ local o = s:option(ListValue, option_name("balancingStrategy"), translate("Balan
o:depends({ [option_name("protocol")] = "_balancing" })
o:value("random")
o:value("leastPing")
o.default = "random"
o:value("leastLoad")
o.default = "leastLoad"
-- 探测地址
local o = s:option(Flag, option_name("useCustomProbeUrl"), translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
o:depends({ [option_name("balancingStrategy")] = "leastPing" })
o:depends({ [option_name("balancingStrategy")] = "leastLoad" })
local o = s:option(Value, option_name("probeUrl"), translate("Probe URL"))
o:depends({ [option_name("useCustomProbeUrl")] = true })
@ -94,6 +97,7 @@ o.description = translate("The URL used to detect the connection status.")
-- 探测间隔
local o = s:option(Value, option_name("probeInterval"), translate("Probe Interval"))
o:depends({ [option_name("balancingStrategy")] = "leastPing" })
o:depends({ [option_name("balancingStrategy")] = "leastLoad" })
o.default = "1m"
o.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
@ -523,4 +527,21 @@ o.default = 0
o = s:option(Flag, option_name("tcpNoDelay"), "tcpNoDelay")
o.default = 0
o = s:option(ListValue, option_name("to_node"), translate("Landing node"), translate("Only support a layer of proxy."))
o.default = ""
o:value("", translate("Close(Not use)"))
for k, v in pairs(nodes_table) do
if v.type == "Xray" then
o:value(v.id, v.remarks)
end
end
for i, v in ipairs(s.fields[option_name("protocol")].keylist) do
if not v:find("_") then
s.fields[option_name("tcpMptcp")]:depends({ [option_name("protocol")] = v })
s.fields[option_name("tcpNoDelay")]:depends({ [option_name("protocol")] = v })
s.fields[option_name("to_node")]:depends({ [option_name("protocol")] = v })
end
end
api.luci_types(arg[1], m, s, type_name, option_prefix)

View File

@ -65,13 +65,13 @@ o.default = "eth1"
o:depends({ [option_name("protocol")] = "_iface" })
local nodes_table = {}
local balancers_table = {}
local iface_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
nodes_table[#nodes_table + 1] = {
id = e[".name"],
remarks = e["remark"]
remarks = e["remark"],
type = e["type"]
}
end
if e.protocol == "_iface" then
@ -89,9 +89,6 @@ if #nodes_table > 0 then
o = s:option(Value, option_name("main_node"), string.format('<a style="color:red">%s</a>', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including <code>Default</code>) has a separate switch that controls whether this rule uses the pre-proxy or not."))
o:depends({ [option_name("protocol")] = "_shunt", [option_name("preproxy_enabled")] = true })
for k, v in pairs(balancers_table) do
o:value(v.id, v.remarks)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remarks)
end
@ -110,9 +107,6 @@ uci:foreach(appname, "shunt_rules", function(e)
o:depends({ [option_name("protocol")] = "_shunt" })
if #nodes_table > 0 then
for k, v in pairs(balancers_table) do
o:value(v.id, v.remarks)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remarks)
end
@ -142,9 +136,6 @@ o:value("_direct", translate("Direct Connection"))
o:value("_blackhole", translate("Blackhole"))
if #nodes_table > 0 then
for k, v in pairs(balancers_table) do
o:value(v.id, v.remarks)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remarks)
end
@ -631,4 +622,18 @@ o:value("prefer_ipv6")
o:value("ipv4_only")
o:value("ipv6_only")
o = s:option(ListValue, option_name("to_node"), translate("Landing node"), translate("Only support a layer of proxy."))
o.default = ""
o:value("", translate("Close(Not use)"))
for k, v in pairs(nodes_table) do
if v.type == "sing-box" then
o:value(v.id, v.remarks)
end
end
for i, v in ipairs(s.fields[option_name("protocol")].keylist) do
if not v:find("_") then
o:depends({ [option_name("protocol")] = v })
end
end
api.luci_types(arg[1], m, s, type_name, option_prefix)

View File

@ -872,6 +872,54 @@ function gen_config(var)
node.address = server_host
node.port = server_port
end
local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
if not node or not outbound or not outbounds_table then return nil end
local default_outTag = outbound.tag
if node.shadowtls == "1" then
local _node = {
type = "sing-box",
protocol = "shadowtls",
shadowtls_version = node.shadowtls_version,
password = (node.shadowtls_version == "2" or node.shadowtls_version == "3") and node.shadowtls_password or nil,
address = node.address,
port = node.port,
tls = "1",
tls_serverName = node.shadowtls_serverName,
utls = node.shadowtls_utls,
fingerprint = node.shadowtls_fingerprint
}
local shadowtls_outbound = gen_outbound(nil, _node, outbound.tag .. "_shadowtls")
if shadowtls_outbound then
table.insert(outbounds_table, shadowtls_outbound)
outbound.detour = outbound.tag .. "_shadowtls"
outbound.server = nil
outbound.server_port = nil
end
end
if node.to_node then
local to_node = uci:get_all(appname, node.to_node)
if to_node then
local to_outbound = gen_outbound(nil, to_node)
if to_outbound then
if shunt_rule_name then
to_outbound.tag = outbound.tag
outbound.tag = node[".name"]
else
to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag
end
to_outbound.detour = outbound.tag
table.insert(outbounds_table, to_outbound)
default_outTag = to_outbound.tag
end
end
end
return default_outTag
end
if node.protocol == "_shunt" then
local rules = {}
@ -900,34 +948,14 @@ function gen_config(var)
elseif preproxy_node and api.is_normal_node(preproxy_node) then
local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag)
if preproxy_outbound then
if preproxy_node.shadowtls == "1" then
local _node = {
type = "sing-box",
protocol = "shadowtls",
shadowtls_version = preproxy_node.shadowtls_version,
password = (preproxy_node.shadowtls_version == "2" or preproxy_node.shadowtls_version == "3") and preproxy_node.shadowtls_password or nil,
address = preproxy_node.address,
port = preproxy_node.port,
tls = "1",
tls_serverName = preproxy_node.shadowtls_serverName,
utls = preproxy_node.shadowtls_utls,
fingerprint = preproxy_node.shadowtls_fingerprint
}
local shadowtls_outbound = gen_outbound(flag, _node, preproxy_tag .. "_shadowtls")
if shadowtls_outbound then
table.insert(outbounds, shadowtls_outbound)
preproxy_outbound.detour = preproxy_outbound.tag .. "_shadowtls"
preproxy_outbound.server = nil
preproxy_outbound.server_port = nil
end
end
set_outbound_detour(preproxy_node, preproxy_outbound, outbounds, preproxy_tag)
table.insert(outbounds, preproxy_outbound)
else
preproxy_enabled = false
end
end
local function gen_shunt_node(rule_name, _node_id, as_proxy)
local function gen_shunt_node(rule_name, _node_id)
if not rule_name then return nil, nil end
if not _node_id then _node_id = node[rule_name] or "nil" end
local rule_outboundTag
@ -999,27 +1027,7 @@ function gen_config(var)
end
local _outbound = gen_outbound(flag, _node, rule_name, { proxy = proxy and 1 or 0, tag = proxy and preproxy_tag or nil })
if _outbound then
if _node.shadowtls == "1" then
local shadowtls_node = {
type = "sing-box",
protocol = "shadowtls",
shadowtls_version = _node.shadowtls_version,
password = (_node.shadowtls_version == "2" or _node.shadowtls_version == "3") and _node.shadowtls_password or nil,
address = _node.address,
port = _node.port,
tls = "1",
tls_serverName = _node.shadowtls_serverName,
utls = _node.shadowtls_utls,
fingerprint = _node.shadowtls_fingerprint
}
local shadowtls_outbound = gen_outbound(flag, shadowtls_node, rule_name .. "_shadowtls", { proxy = proxy and 1 or 0, tag = proxy and preproxy_tag or nil })
if shadowtls_outbound then
table.insert(outbounds, shadowtls_outbound)
_outbound.detour = _outbound.tag .. "_shadowtls"
_outbound.server = nil
_outbound.server_port = nil
end
end
set_outbound_detour(_node, _outbound, outbounds, rule_name)
table.insert(outbounds, _outbound)
rule_outboundTag = rule_name
end
@ -1196,46 +1204,23 @@ function gen_config(var)
for index, value in ipairs(rules) do
table.insert(route.rules, rules[index])
end
else
local outbound = nil
if node.protocol == "_iface" then
if node.iface then
outbound = {
type = "direct",
tag = node_id,
bind_interface = node.iface,
routing_mark = 255,
}
sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
end
else
outbound = gen_outbound(flag, node)
if outbound then
if node.shadowtls == "1" then
local shadowtls_node = {
type = "sing-box",
protocol = "shadowtls",
shadowtls_version = node.shadowtls_version,
password = (node.shadowtls_version == "2" or node.shadowtls_version == "3") and node.shadowtls_password or nil,
address = node.address,
port = node.port,
tls = "1",
tls_serverName = node.shadowtls_serverName,
utls = node.shadowtls_utls,
fingerprint = node.shadowtls_fingerprint
}
local shadowtls_outbound = gen_outbound(flag, shadowtls_node, outbound.tag .. "_shadowtls")
if shadowtls_outbound then
table.insert(outbounds, shadowtls_outbound)
outbound.detour = outbound.tag .. "_shadowtls"
outbound.server = nil
outbound.server_port = nil
end
end
end
end
if outbound then
elseif node.protocol == "_iface" then
if node.iface then
local outbound = {
type = "direct",
tag = node_id,
bind_interface = node.iface,
routing_mark = 255,
}
table.insert(outbounds, outbound)
default_outTag = outbound.tag
route.final = default_outTag
sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
end
else
local outbound = gen_outbound(flag, node)
if outbound then
default_outTag = set_outbound_detour(node, outbound, outbounds)
table.insert(outbounds, outbound)
route.final = default_outTag
end

View File

@ -245,6 +245,11 @@ function gen_outbound(flag, node, tag, proxy_table)
reserved = (node.protocol == "wireguard" and node.wireguard_reserved) and node.wireguard_reserved or nil
}
}
if node.protocol == "wireguard" then
result.settings.kernelMode = false
end
local alpn = {}
if node.alpn and node.alpn ~= "default" then
string.gsub(node.alpn, '[^' .. "," .. ']+', function(w)
@ -658,7 +663,7 @@ function gen_config(var)
selector = valid_nodes,
strategy = { type = _node.balancingStrategy or "random" }
}
if _node.balancingStrategy == "leastPing" then
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" then
if not observatory then
observatory = {
subjectSelector = { "blc-" },
@ -685,6 +690,34 @@ function gen_config(var)
return balancer, rule
end
local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
if not node or not outbound or not outbounds_table then return nil end
local default_outTag = outbound.tag
if node.to_node then
local to_node = uci:get_all(appname, node.to_node)
if to_node then
local to_outbound = gen_outbound(nil, to_node)
if to_outbound then
if shunt_rule_name then
to_outbound.tag = outbound.tag
outbound.tag = node[".name"]
else
to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag
end
to_outbound.proxySettings = {
tag = outbound.tag,
transportLayer = true
}
table.insert(outbounds_table, to_outbound)
default_outTag = to_outbound.tag
end
end
end
return default_outTag
end
for k, v in pairs(nodes) do
if server_host and server_port then
v.address = server_host
@ -722,6 +755,7 @@ function gen_config(var)
elseif preproxy_node and api.is_normal_node(preproxy_node) then
local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag, { fragment = xray_settings.fragment == "1" or nil })
if preproxy_outbound then
set_outbound_detour(preproxy_node, preproxy_outbound, outbounds, preproxy_tag)
table.insert(outbounds, preproxy_outbound)
else
preproxy_enabled = false
@ -737,7 +771,7 @@ function gen_config(var)
end
end
local function gen_shunt_node(rule_name, _node_id, as_proxy)
local function gen_shunt_node(rule_name, _node_id)
if not rule_name then return nil, nil end
if not _node_id then _node_id = node[rule_name] or "nil" end
local rule_outboundTag
@ -831,6 +865,7 @@ function gen_config(var)
end
local _outbound = gen_outbound(flag, _node, rule_name, proxy_table)
if _outbound then
set_outbound_detour(_node, _outbound, outbounds, rule_name)
table.insert(outbounds, _outbound)
if proxy then preproxy_used = true end
rule_outboundTag = rule_name
@ -991,39 +1026,39 @@ function gen_config(var)
}
}
end
else
local outbound = nil
if node.protocol == "_iface" then
if node.iface then
outbound = {
protocol = "freedom",
tag = "outbound",
streamSettings = {
sockopt = {
mark = 255,
interface = node.iface
}
elseif node.protocol == "_iface" then
if node.iface then
local outbound = {
protocol = "freedom",
tag = "outbound",
streamSettings = {
sockopt = {
mark = 255,
interface = node.iface
}
}
sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
end
else
outbound = gen_outbound(flag, node, nil, { fragment = xray_settings.fragment == "1" or nil })
}
table.insert(outbounds, outbound)
sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
end
else
local outbound = gen_outbound(flag, node, nil, { fragment = xray_settings.fragment == "1" or nil })
if outbound then
local default_outTag = set_outbound_detour(node, outbound, outbounds)
table.insert(outbounds, outbound)
routing = {
domainStrategy = "AsIs",
domainMatcher = "hybrid",
rules = {}
}
table.insert(routing.rules, {
_flag = "default",
type = "field",
outboundTag = default_outTag,
network = "tcp,udp"
})
end
if outbound then table.insert(outbounds, outbound) end
routing = {
domainStrategy = "AsIs",
domainMatcher = "hybrid",
rules = {}
}
table.insert(routing.rules, {
_flag = "default",
type = "field",
outboundTag = node_id,
network = "tcp,udp"
})
end
end
if remote_dns_udp_server then

View File

@ -225,6 +225,10 @@ local api = require "luci.passwall2.api"
if (v_transport === "ws") {
info.host = opt.get(dom_prefix + "ws_host").value;
info.path = opt.get(dom_prefix + "ws_path").value;
if (v_type == "sing-box" && opt.get(dom_prefix + "ws_enableEarlyData").checked) {
var ws_maxEarlyData = opt.get(dom_prefix + "ws_maxEarlyData").value;
info.path = info.path + "?ed=" + ws_maxEarlyData;
}
} else if (v_transport === "h2") {
info.host = opt.get(dom_prefix + "h2_host").value;
info.path = opt.get(dom_prefix + "h2_path").value;
@ -269,6 +273,10 @@ local api = require "luci.passwall2.api"
if (v_transport === "ws") {
params += opt.query("host", dom_prefix + "ws_host");
params += opt.query("path", dom_prefix + "ws_path");
if (v_type == "sing-box" && opt.get(dom_prefix + "ws_enableEarlyData").checked) {
var ws_maxEarlyData = opt.get(dom_prefix + "ws_maxEarlyData").value;
params += "?ed=" + ws_maxEarlyData;
}
} else if (v_transport === "h2") {
v_transport = "http";
params += opt.query("host", dom_prefix + "h2_host");
@ -720,6 +728,24 @@ local api = require "luci.passwall2.api"
} else if (ssm.net === "ws") {
opt.set(dom_prefix + 'ws_host', ssm.host);
opt.set(dom_prefix + 'ws_path', ssm.path);
if (dom_prefix == "singbox_" && ssm.path && ssm.path.length > 1) {
var ws_path_params = {};
var ws_path_dat = ssm.path.split('?');
var ws_path = ws_path_dat[0];
var ws_path_params = {};
var ws_path_params_array = ws_path_dat[1].split('&');
for (i = 0; i < ws_path_params_array.length; i++) {
var kv = ws_path_params_array[i].split('=');
ws_path_params[decodeURIComponent(kv[0]).toLowerCase()] = decodeURIComponent(kv[1] || '');
}
if (ws_path_params.ed) {
opt.set(dom_prefix + 'ws_path', ws_path);
opt.set(dom_prefix + 'ws_enableEarlyData', true);
opt.set(dom_prefix + 'ws_maxEarlyData', ws_path_params.ed);
opt.set(dom_prefix + 'ws_earlyDataHeaderName', 'Sec-WebSocket-Protocol');
}
}
} else if (ssm.net === "h2") {
opt.set(dom_prefix + 'h2_host', ssm.host);
opt.set(dom_prefix + 'h2_path', ssm.path);
@ -809,6 +835,24 @@ local api = require "luci.passwall2.api"
} else if (queryParam.type === "ws") {
opt.set(dom_prefix + 'ws_host', queryParam.host || "");
opt.set(dom_prefix + 'ws_path', queryParam.path || "");
if (dom_prefix == "singbox_" && queryParam.path && queryParam.path.length > 1) {
var ws_path_params = {};
var ws_path_dat = queryParam.path.split('?');
var ws_path = ws_path_dat[0];
var ws_path_params = {};
var ws_path_params_array = ws_path_dat[1].split('&');
for (i = 0; i < ws_path_params_array.length; i++) {
var kv = ws_path_params_array[i].split('=');
ws_path_params[decodeURIComponent(kv[0]).toLowerCase()] = decodeURIComponent(kv[1] || '');
}
if (ws_path_params.ed) {
opt.set(dom_prefix + 'ws_path', ws_path);
opt.set(dom_prefix + 'ws_enableEarlyData', true);
opt.set(dom_prefix + 'ws_maxEarlyData', ws_path_params.ed);
opt.set(dom_prefix + 'ws_earlyDataHeaderName', 'Sec-WebSocket-Protocol');
}
}
} else if (queryParam.type === "h2" || queryParam.type === "http") {
opt.set(dom_prefix + 'h2_host', queryParam.host || "");
opt.set(dom_prefix + 'h2_path', queryParam.path || "");

View File

@ -1482,3 +1482,9 @@ msgstr "分片间隔ms"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "如果是域名,域名将在请求发出之前解析为 IP。"
msgid "Landing node"
msgstr "落地节点"
msgid "Only support a layer of proxy."
msgstr "仅支持一层代理。"

View File

@ -401,6 +401,21 @@ local function processData(szType, content, add_mode, add_from)
if info.net == 'ws' then
result.ws_host = info.host
result.ws_path = info.path
if result.type == "sing-box" and info.path then
local ws_path_dat = split(info.path, "?")
local ws_path = ws_path_dat[1]
local ws_path_params = {}
for _, v in pairs(split(ws_path_dat[2], '&')) do
local t = split(v, '=')
ws_path_params[t[1]] = t[2]
end
if ws_path_params.ed and tonumber(ws_path_params.ed) then
result.ws_path = ws_path
result.ws_enableEarlyData = "1"
result.ws_maxEarlyData = tonumber(ws_path_params.ed)
result.ws_earlyDataHeaderName = "Sec-WebSocket-Protocol"
end
end
end
if info.net == 'h2' then
result.h2_host = info.host
@ -672,6 +687,21 @@ local function processData(szType, content, add_mode, add_from)
if params.type == 'ws' then
result.ws_host = params.host
result.ws_path = params.path
if result.type == "sing-box" and params.path then
local ws_path_dat = split(params.path, "?")
local ws_path = ws_path_dat[1]
local ws_path_params = {}
for _, v in pairs(split(ws_path_dat[2], '&')) do
local t = split(v, '=')
ws_path_params[t[1]] = t[2]
end
if ws_path_params.ed and tonumber(ws_path_params.ed) then
result.ws_path = ws_path
result.ws_enableEarlyData = "1"
result.ws_maxEarlyData = tonumber(ws_path_params.ed)
result.ws_earlyDataHeaderName = "Sec-WebSocket-Protocol"
end
end
end
if params.type == 'h2' or params.type == 'http' then
params.type = "h2"

View File

@ -13,9 +13,9 @@
if (s)
{
if (rv.ret=="0")
s.innerHTML ="<font color='green'>"+"<%:Connect OK%>"+"</font>";
s.innerHTML ="<font style=\'color:green\'>"+"<%:Connect OK%>"+"</font>";
else
s.innerHTML ="<font color='red'>"+"<%:Connect Error%>"+"</font>";
s.innerHTML ="<font style=\'color:red\'>"+"<%:Connect Error%>"+"</font>";
}
btn.disabled = false;
btn.value = '<%:Check Connect%>';

View File

@ -15,13 +15,13 @@
switch (rv.ret)
{
case 0:
s.innerHTML ="<font color='green'>"+"<%:Refresh OK!%> "+"<%:Total Records:%>"+rv.retcount+"</font>";
s.innerHTML ="<font style=\'color:green\'>"+"<%:Refresh OK!%> "+"<%:Total Records:%>"+rv.retcount+"</font>";
break;
case 1:
s.innerHTML ="<font color='green'>"+"<%:No new data!%> "+"</font>";
s.innerHTML ="<font style=\'color:green\'>"+"<%:No new data!%> "+"</font>";
break;
default:
s.innerHTML ="<font color='red'>"+"<%:Refresh Error!%> "+"</font>";
s.innerHTML ="<font style=\'color:red\'>"+"<%:Refresh Error!%> "+"</font>";
break;
}
}

View File

@ -7,7 +7,7 @@
return false;
}
if (reset != "reset") {
s.innerHTML = "<font color='red'><%:The content entered is incorrect!%></font>";
s.innerHTML = "<font style=\'color:red\'><%:The content entered is incorrect!%></font>";
return false;
}
btn.disabled = true;
@ -15,7 +15,7 @@
murl=dataname;
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr","reset")%>', { set:murl }, function(x,rv) {
btn.value = '<%:Reset complete%>';
s.innerHTML = "<font color='green'><%:Reset complete%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Reset complete%></font>";
});
return false;
}

View File

@ -18,7 +18,7 @@
const wsPath = wsPaths[index];
const tls = tlss[index];
if (!dom) res()
port.innerHTML = '<font color="#0072c3">connect</font>';
port.innerHTML = '<font style=\"color:#0072c3\">connect</font>';
XHR.get('<%=luci.dispatcher.build_url("admin/services/shadowsocksr/ping")%>', {
index,
domain: dom.getAttribute("hint"),
@ -34,11 +34,11 @@
if (result.ping < 200) col = '#ff7700';
if (result.ping < 100) col = '#249400';
}
dom.innerHTML = `<font color="${col}">${(result.ping ? result.ping : "--") + " ms"}</font>`
dom.innerHTML = `<font style=\"color:${col}\">${(result.ping ? result.ping : "--") + " ms"}</font>`
if (result.socket) {
port.innerHTML = '<font color="#249400">ok</font>';
port.innerHTML = '<font style=\"color:#249400\">ok</font>';
} else {
port.innerHTML = '<font color="#ff0000">fail</font>';
port.innerHTML = '<font style=\"color:#ff0000\">fail</font>';
}
res();
});

View File

@ -69,9 +69,9 @@ function export_ssr_url(btn, urlname, sid) {
textarea.select();
try {
document.execCommand("copy"); // Security exception may be thrown by some browsers.
s.innerHTML = "<font color='green'><%:Copy SSR to clipboard successfully.%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Copy SSR to clipboard successfully.%></font>";
} catch (ex) {
s.innerHTML = "<font color='red'><%:Unable to copy SSR to clipboard.%></font>";
s.innerHTML = "<font style=\'color:red\'><%:Unable to copy SSR to clipboard.%></font>";
} finally {
document.body.removeChild(textarea);
}
@ -83,7 +83,7 @@ function import_ssr_url(btn, urlname, sid) {
if (!s) return false;
var ssrurl = prompt("<%:Paste sharing link here%>", "");
if (ssrurl == null || ssrurl == "") {
s.innerHTML = "<font color='red'><%:User cancelled.%></font>";
s.innerHTML = "<font style=\'color:red\'><%:User cancelled.%></font>";
return false;
}
s.innerHTML = "";
@ -118,7 +118,7 @@ function import_ssr_url(btn, urlname, sid) {
document.getElementsByName('cbid.shadowsocksr.' + sid + '.insecure')[0].checked = params.get("insecure") ? true : false;
document.getElementsByName('cbid.shadowsocksr.' + sid + '.alias')[0].value = url.hash ? decodeURIComponent(url.hash.slice(1)) : "";
s.innerHTML = "<font color='green'><%:Import configuration information successfully.%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Import configuration information successfully.%></font>";
return false;
case "ss":
var url0, param = "";
@ -164,7 +164,7 @@ function import_ssr_url(btn, urlname, sid) {
if (param != undefined) {
document.getElementsByName('cbid.shadowsocksr.' + sid + '.alias')[0].value = decodeURI(param);
}
s.innerHTML = "<font color='green'><%:Import configuration information successfully.%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Import configuration information successfully.%></font>";
} else {
var sstr = b64decsafe(url0);
document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = ssu[0];
@ -179,7 +179,7 @@ function import_ssr_url(btn, urlname, sid) {
if (param != undefined) {
document.getElementsByName('cbid.shadowsocksr.' + sid + '.alias')[0].value = decodeURI(param);
}
s.innerHTML = "<font color='green'><%:Import configuration information successfully.%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Import configuration information successfully.%></font>";
}
return false;
case "ssr":
@ -212,7 +212,7 @@ function import_ssr_url(btn, urlname, sid) {
document.getElementsByName('cbid.shadowsocksr.' + sid + '.protocol_param')[0].value = dictvalue(pdict, 'protoparam');
var rem = pdict['remarks'];
if (typeof (rem) != 'undefined' && rem != '' && rem.length > 0) document.getElementsByName('cbid.shadowsocksr.' + sid + '.alias')[0].value = b64decutf8safe(rem);
s.innerHTML = "<font color='green'><%:Import configuration information successfully.%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Import configuration information successfully.%></font>";
return false;
case "trojan":
try {
@ -234,7 +234,7 @@ function import_ssr_url(btn, urlname, sid) {
document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls')[0].dispatchEvent(event);
document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls_host')[0].value = url.searchParams.get("sni");
s.innerHTML = "<font color='green'><%:Import configuration information successfully.%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Import configuration information successfully.%></font>";
return false;
case "vmess":
var sstr = b64DecodeUnicode(ssu[1]);
@ -287,7 +287,7 @@ function import_ssr_url(btn, urlname, sid) {
}
document.getElementsByName('cbid.shadowsocksr.' + sid + '.mux')[0].checked = true;
document.getElementsByName('cbid.shadowsocksr.' + sid + '.mux')[0].dispatchEvent(event);
s.innerHTML = "<font color='green'><%:Import configuration information successfully.%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Import configuration information successfully.%></font>";
return false;
case "vless":
try {
@ -357,10 +357,10 @@ function import_ssr_url(btn, urlname, sid) {
}
break;
}
s.innerHTML = "<font color='green'><%:Import configuration information successfully.%></font>";
s.innerHTML = "<font style=\'color:green\'><%:Import configuration information successfully.%></font>";
return false;
default:
s.innerHTML = "<font color='red'><%:Invalid format.%></font>";
s.innerHTML = "<font style=\'color:red\'><%:Invalid format.%></font>";
return false;
}
}