update 2025-04-29 09:45:01
This commit is contained in:
parent
2cd25398e5
commit
91c7cc9ee4
|
@ -121,6 +121,8 @@ o.cfgvalue = function(t, n)
|
||||||
protocol = "HY"
|
protocol = "HY"
|
||||||
elseif protocol == "hysteria2" then
|
elseif protocol == "hysteria2" then
|
||||||
protocol = "HY2"
|
protocol = "HY2"
|
||||||
|
elseif protocol == "anytls" then
|
||||||
|
protocol = "AnyTLS"
|
||||||
else
|
else
|
||||||
protocol = protocol:gsub("^%l",string.upper)
|
protocol = protocol:gsub("^%l",string.upper)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,9 @@ if not singbox_bin then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local local_version = api.get_app_version("sing-box")
|
||||||
|
local version_ge_1_12_0 = api.compare_versions(local_version:match("[^v]+"), ">=", "1.12.0")
|
||||||
|
|
||||||
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
|
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
|
||||||
|
|
||||||
local appname = "passwall"
|
local appname = "passwall"
|
||||||
|
@ -56,6 +59,9 @@ end
|
||||||
if singbox_tags:find("with_quic") then
|
if singbox_tags:find("with_quic") then
|
||||||
o:value("hysteria2", "Hysteria2")
|
o:value("hysteria2", "Hysteria2")
|
||||||
end
|
end
|
||||||
|
if version_ge_1_12_0 then
|
||||||
|
o:value("anytls", "AnyTLS")
|
||||||
|
end
|
||||||
o:value("_urltest", translate("URLTest"))
|
o:value("_urltest", translate("URLTest"))
|
||||||
o:value("_shunt", translate("Shunt"))
|
o:value("_shunt", translate("Shunt"))
|
||||||
o:value("_iface", translate("Custom Interface"))
|
o:value("_iface", translate("Custom Interface"))
|
||||||
|
@ -248,6 +254,7 @@ o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||||
o:depends({ [_n("protocol")] = "shadowsocksr" })
|
o:depends({ [_n("protocol")] = "shadowsocksr" })
|
||||||
o:depends({ [_n("protocol")] = "trojan" })
|
o:depends({ [_n("protocol")] = "trojan" })
|
||||||
o:depends({ [_n("protocol")] = "tuic" })
|
o:depends({ [_n("protocol")] = "tuic" })
|
||||||
|
o:depends({ [_n("protocol")] = "anytls" })
|
||||||
|
|
||||||
o = s:option(ListValue, _n("security"), translate("Encrypt Method"))
|
o = s:option(ListValue, _n("security"), translate("Encrypt Method"))
|
||||||
for a, t in ipairs(security_list) do o:value(t) end
|
for a, t in ipairs(security_list) do o:value(t) end
|
||||||
|
@ -428,6 +435,7 @@ o:depends({ [_n("protocol")] = "vless" })
|
||||||
o:depends({ [_n("protocol")] = "http" })
|
o:depends({ [_n("protocol")] = "http" })
|
||||||
o:depends({ [_n("protocol")] = "trojan" })
|
o:depends({ [_n("protocol")] = "trojan" })
|
||||||
o:depends({ [_n("protocol")] = "shadowsocks" })
|
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.default = "default"
|
||||||
|
@ -513,6 +521,7 @@ if singbox_tags:find("with_utls") then
|
||||||
o:depends({ [_n("protocol")] = "shadowsocks", [_n("utls")] = true })
|
o:depends({ [_n("protocol")] = "shadowsocks", [_n("utls")] = true })
|
||||||
o:depends({ [_n("protocol")] = "socks", [_n("utls")] = true })
|
o:depends({ [_n("protocol")] = "socks", [_n("utls")] = true })
|
||||||
o:depends({ [_n("protocol")] = "trojan", [_n("utls")] = true })
|
o:depends({ [_n("protocol")] = "trojan", [_n("utls")] = true })
|
||||||
|
o:depends({ [_n("protocol")] = "anytls", [_n("utls")] = true })
|
||||||
|
|
||||||
o = s:option(Value, _n("reality_publicKey"), translate("Public Key"))
|
o = s:option(Value, _n("reality_publicKey"), translate("Public Key"))
|
||||||
o:depends({ [_n("utls")] = true, [_n("reality")] = true })
|
o:depends({ [_n("utls")] = true, [_n("reality")] = true })
|
||||||
|
@ -731,6 +740,7 @@ o:depends({ [_n("protocol")] = "hysteria" })
|
||||||
o:depends({ [_n("protocol")] = "vless" })
|
o:depends({ [_n("protocol")] = "vless" })
|
||||||
o:depends({ [_n("protocol")] = "tuic" })
|
o:depends({ [_n("protocol")] = "tuic" })
|
||||||
o:depends({ [_n("protocol")] = "hysteria2" })
|
o:depends({ [_n("protocol")] = "hysteria2" })
|
||||||
|
o:depends({ [_n("protocol")] = "anytls" })
|
||||||
|
|
||||||
o = s:option(ListValue, _n("chain_proxy"), translate("Chain Proxy"))
|
o = s:option(ListValue, _n("chain_proxy"), translate("Chain Proxy"))
|
||||||
o:value("", translate("Close(Not use)"))
|
o:value("", translate("Close(Not use)"))
|
||||||
|
|
|
@ -8,6 +8,9 @@ if not singbox_bin then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local local_version = api.get_app_version("sing-box")
|
||||||
|
local version_ge_1_12_0 = api.compare_versions(local_version:match("[^v]+"), ">=", "1.12.0")
|
||||||
|
|
||||||
local fs = api.fs
|
local fs = api.fs
|
||||||
|
|
||||||
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
|
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
|
||||||
|
@ -47,6 +50,9 @@ end
|
||||||
if singbox_tags:find("with_quic") then
|
if singbox_tags:find("with_quic") then
|
||||||
o:value("hysteria2", "Hysteria2")
|
o:value("hysteria2", "Hysteria2")
|
||||||
end
|
end
|
||||||
|
if version_ge_1_12_0 then
|
||||||
|
o:value("anytls", "AnyTLS")
|
||||||
|
end
|
||||||
o:value("direct", "Direct")
|
o:value("direct", "Direct")
|
||||||
|
|
||||||
o = s:option(Value, _n("port"), translate("Listen Port"))
|
o = s:option(Value, _n("port"), translate("Listen Port"))
|
||||||
|
@ -70,6 +76,7 @@ o:depends({ [_n("protocol")] = "http" })
|
||||||
o = s:option(Value, _n("username"), translate("Username"))
|
o = s:option(Value, _n("username"), translate("Username"))
|
||||||
o:depends({ [_n("auth")] = true })
|
o:depends({ [_n("auth")] = true })
|
||||||
o:depends({ [_n("protocol")] = "naive" })
|
o:depends({ [_n("protocol")] = "naive" })
|
||||||
|
o:depends({ [_n("protocol")] = "anytls" })
|
||||||
|
|
||||||
o = s:option(Value, _n("password"), translate("Password"))
|
o = s:option(Value, _n("password"), translate("Password"))
|
||||||
o.password = true
|
o.password = true
|
||||||
|
@ -77,6 +84,7 @@ o:depends({ [_n("auth")] = true })
|
||||||
o:depends({ [_n("protocol")] = "shadowsocks" })
|
o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||||
o:depends({ [_n("protocol")] = "naive" })
|
o:depends({ [_n("protocol")] = "naive" })
|
||||||
o:depends({ [_n("protocol")] = "tuic" })
|
o:depends({ [_n("protocol")] = "tuic" })
|
||||||
|
o:depends({ [_n("protocol")] = "anytls" })
|
||||||
|
|
||||||
if singbox_tags:find("with_quic") then
|
if singbox_tags:find("with_quic") then
|
||||||
o = s:option(Value, _n("hysteria_up_mbps"), translate("Max upload Mbps"))
|
o = s:option(Value, _n("hysteria_up_mbps"), translate("Max upload Mbps"))
|
||||||
|
@ -220,6 +228,7 @@ o:depends({ [_n("protocol")] = "http" })
|
||||||
o:depends({ [_n("protocol")] = "vmess" })
|
o:depends({ [_n("protocol")] = "vmess" })
|
||||||
o:depends({ [_n("protocol")] = "vless" })
|
o:depends({ [_n("protocol")] = "vless" })
|
||||||
o:depends({ [_n("protocol")] = "trojan" })
|
o:depends({ [_n("protocol")] = "trojan" })
|
||||||
|
o:depends({ [_n("protocol")] = "anytls" })
|
||||||
|
|
||||||
if singbox_tags:find("with_reality_server") then
|
if singbox_tags:find("with_reality_server") then
|
||||||
-- [[ REALITY部分 ]] --
|
-- [[ REALITY部分 ]] --
|
||||||
|
@ -229,6 +238,7 @@ if singbox_tags:find("with_reality_server") then
|
||||||
o:depends({ [_n("protocol")] = "vmess", [_n("tls")] = true })
|
o:depends({ [_n("protocol")] = "vmess", [_n("tls")] = true })
|
||||||
o:depends({ [_n("protocol")] = "vless", [_n("tls")] = true })
|
o:depends({ [_n("protocol")] = "vless", [_n("tls")] = true })
|
||||||
o:depends({ [_n("protocol")] = "trojan", [_n("tls")] = true })
|
o:depends({ [_n("protocol")] = "trojan", [_n("tls")] = true })
|
||||||
|
o:depends({ [_n("protocol")] = "anytls", [_n("tls")] = true })
|
||||||
|
|
||||||
o = s:option(Value, _n("reality_private_key"), translate("Private Key"))
|
o = s:option(Value, _n("reality_private_key"), translate("Private Key"))
|
||||||
o:depends({ [_n("reality")] = true })
|
o:depends({ [_n("reality")] = true })
|
||||||
|
|
|
@ -522,6 +522,8 @@ function get_valid_nodes()
|
||||||
protocol = "HY"
|
protocol = "HY"
|
||||||
elseif protocol == "hysteria2" then
|
elseif protocol == "hysteria2" then
|
||||||
protocol = "HY2"
|
protocol = "HY2"
|
||||||
|
elseif protocol == "anytls" then
|
||||||
|
protocol = "AnyTLS"
|
||||||
else
|
else
|
||||||
protocol = protocol:gsub("^%l",string.upper)
|
protocol = protocol:gsub("^%l",string.upper)
|
||||||
end
|
end
|
||||||
|
@ -566,6 +568,8 @@ function get_node_remarks(n)
|
||||||
protocol = "HY"
|
protocol = "HY"
|
||||||
elseif protocol == "hysteria2" then
|
elseif protocol == "hysteria2" then
|
||||||
protocol = "HY2"
|
protocol = "HY2"
|
||||||
|
elseif protocol == "anytls" then
|
||||||
|
protocol = "AnyTLS"
|
||||||
else
|
else
|
||||||
protocol = protocol:gsub("^%l",string.upper)
|
protocol = protocol:gsub("^%l",string.upper)
|
||||||
end
|
end
|
||||||
|
|
|
@ -441,6 +441,13 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if node.protocol == "anytls" then
|
||||||
|
protocol_table = {
|
||||||
|
password = (node.password and node.password ~= "") and node.password or "",
|
||||||
|
tls = tls
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
if protocol_table then
|
if protocol_table then
|
||||||
for key, value in pairs(protocol_table) do
|
for key, value in pairs(protocol_table) do
|
||||||
result[key] = value
|
result[key] = value
|
||||||
|
@ -730,6 +737,18 @@ function gen_config_server(node)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if node.protocol == "anytls" then
|
||||||
|
protocol_table = {
|
||||||
|
users = {
|
||||||
|
{
|
||||||
|
name = (node.username and node.username ~= "") and node.username or "sekai",
|
||||||
|
password = node.password
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tls = tls,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
if node.protocol == "direct" then
|
if node.protocol == "direct" then
|
||||||
protocol_table = {
|
protocol_table = {
|
||||||
network = (node.d_protocol ~= "TCP,UDP") and node.d_protocol or nil,
|
network = (node.d_protocol ~= "TCP,UDP") and node.d_protocol or nil,
|
||||||
|
|
|
@ -546,6 +546,41 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin
|
||||||
params += opt.query("congestion_control", dom_prefix + "tuic_congestion_control");
|
params += opt.query("congestion_control", dom_prefix + "tuic_congestion_control");
|
||||||
params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure");
|
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 === "anytls") {
|
||||||
|
protocol = "anytls";
|
||||||
|
var v_password = opt.get(dom_prefix + "password");
|
||||||
|
var v_port = opt.get(dom_prefix + "port");
|
||||||
|
url = encodeURIComponent(v_password.value) +
|
||||||
|
"@" + _address +
|
||||||
|
":" + v_port.value + "?";
|
||||||
|
|
||||||
|
var params = "";
|
||||||
|
if (opt.get(dom_prefix + "tls").checked) {
|
||||||
|
var v_security = "tls";
|
||||||
|
if (opt.get(dom_prefix + "fingerprint") && opt.get(dom_prefix + "fingerprint").value != "") {
|
||||||
|
let v_fp = opt.get(dom_prefix + "fingerprint").value;
|
||||||
|
params += "&fp=" + v_fp;
|
||||||
|
}
|
||||||
|
if (opt.get(dom_prefix + "reality") && opt.get(dom_prefix + "reality").checked) {
|
||||||
|
v_security = "reality";
|
||||||
|
if (opt.get(dom_prefix + "fingerprint") && opt.get(dom_prefix + "fingerprint").value != "") {
|
||||||
|
let v_fp = opt.get(dom_prefix + "fingerprint").value;
|
||||||
|
params += "&fp=" + v_fp;
|
||||||
|
}
|
||||||
|
params += opt.query("pbk", dom_prefix + "reality_publicKey");
|
||||||
|
params += opt.query("sid", dom_prefix + "reality_shortId");
|
||||||
|
}
|
||||||
|
params += "&security=" + v_security;
|
||||||
|
params += opt.query("alpn", dom_prefix + "alpn");
|
||||||
|
params += opt.query("sni", dom_prefix + "tls_serverName");
|
||||||
|
params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure");
|
||||||
|
}
|
||||||
|
|
||||||
params += "#" + encodeURI(v_alias.value);
|
params += "#" + encodeURI(v_alias.value);
|
||||||
if (params[0] == "&") {
|
if (params[0] == "&") {
|
||||||
params = params.substring(1);
|
params = params.substring(1);
|
||||||
|
@ -1373,6 +1408,66 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin
|
||||||
opt.set('remarks', decodeURIComponent(hash.substr(1)));
|
opt.set('remarks', decodeURIComponent(hash.substr(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ssu[0] === "anytls") {
|
||||||
|
if (has_singbox) {
|
||||||
|
dom_prefix = "singbox_"
|
||||||
|
opt.set('type', "sing-box");
|
||||||
|
}
|
||||||
|
opt.set(dom_prefix + 'protocol', "anytls");
|
||||||
|
var m = parseNodeUrl(ssrurl);
|
||||||
|
var password = m.passwd;
|
||||||
|
if (password === "") {
|
||||||
|
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
opt.set(dom_prefix + 'password', password);
|
||||||
|
opt.set(dom_prefix + 'address', m.hostname);
|
||||||
|
opt.set(dom_prefix + 'port', m.port || "443");
|
||||||
|
var queryParam = {};
|
||||||
|
if (m.search.length > 1) {
|
||||||
|
var query = m.search.replace('/?', '?').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] || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (queryParam.security) {
|
||||||
|
if (queryParam.security == "tls") {
|
||||||
|
opt.set(dom_prefix + 'tls', true);
|
||||||
|
opt.set(dom_prefix + 'reality', false);
|
||||||
|
opt.set(dom_prefix + 'flow', queryParam.flow || '');
|
||||||
|
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' || queryParam.insecure === '0') {
|
||||||
|
opt.set(dom_prefix + 'tls_allowInsecure', false);
|
||||||
|
}
|
||||||
|
if (queryParam.fp && queryParam.fp.trim() != "") {
|
||||||
|
opt.set(dom_prefix + 'utls', true);
|
||||||
|
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (queryParam.security == "reality") {
|
||||||
|
opt.set(dom_prefix + 'tls', true);
|
||||||
|
opt.set(dom_prefix + 'reality', true);
|
||||||
|
opt.set(dom_prefix + 'flow', queryParam.flow || '');
|
||||||
|
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
|
||||||
|
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
|
||||||
|
if (queryParam.fp && queryParam.fp.trim() != "") {
|
||||||
|
opt.set(dom_prefix + 'utls', true);
|
||||||
|
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
|
||||||
|
}
|
||||||
|
opt.set(dom_prefix + 'reality_publicKey', queryParam.pbk || '');
|
||||||
|
opt.set(dom_prefix + 'reality_shortId', queryParam.sid || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m.hash) {
|
||||||
|
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (dom_prefix && dom_prefix != null) {
|
if (dom_prefix && dom_prefix != null) {
|
||||||
if (opt.get(dom_prefix + 'port').value) {
|
if (opt.get(dom_prefix + 'port').value) {
|
||||||
opt.get(dom_prefix + 'port').focus();
|
opt.get(dom_prefix + 'port').focus();
|
||||||
|
|
|
@ -1290,6 +1290,70 @@ local function processData(szType, content, add_mode, add_from)
|
||||||
end
|
end
|
||||||
result.type = 'sing-box'
|
result.type = 'sing-box'
|
||||||
result.protocol = "tuic"
|
result.protocol = "tuic"
|
||||||
|
elseif szType == "anytls" then
|
||||||
|
result.type = 'sing-box'
|
||||||
|
result.protocol = "anytls"
|
||||||
|
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)
|
||||||
|
if content:find("@") then
|
||||||
|
local Info = split(content, "@")
|
||||||
|
result.password = UrlDecode(Info[1])
|
||||||
|
local port = "443"
|
||||||
|
Info[2] = (Info[2] or ""):gsub("/%?", "?")
|
||||||
|
local query = split(Info[2], "?")
|
||||||
|
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])
|
||||||
|
end
|
||||||
|
-- [2001:4860:4860::8888]:443
|
||||||
|
-- 8.8.8.8:443
|
||||||
|
if host_port:find(":") then
|
||||||
|
local sp = split(host_port, ":")
|
||||||
|
port = sp[#sp]
|
||||||
|
if api.is_ipv6addrport(host_port) then
|
||||||
|
result.address = api.get_ipv6_only(host_port)
|
||||||
|
else
|
||||||
|
result.address = sp[1]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result.address = host_port
|
||||||
|
end
|
||||||
|
result.tls = "0"
|
||||||
|
if params.security == "tls" or params.security == "reality" then
|
||||||
|
result.tls = "1"
|
||||||
|
result.tls_serverName = (params.sni and params.sni ~= "") and params.sni or params.host
|
||||||
|
result.alpn = params.alpn
|
||||||
|
if params.fp and params.fp ~= "" then
|
||||||
|
result.utls = "1"
|
||||||
|
result.fingerprint = params.fp
|
||||||
|
end
|
||||||
|
if params.security == "reality" then
|
||||||
|
result.reality = "1"
|
||||||
|
result.reality_publicKey = params.pbk or nil
|
||||||
|
result.reality_shortId = params.sid or nil
|
||||||
|
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
|
||||||
|
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("跳过节点:" .. result.remarks ..",因" .. szType .. "类型的节点需要 Sing-Box 1.12 以上版本支持。")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
log('暂时不支持' .. szType .. "类型的节点订阅,跳过此节点。")
|
log('暂时不支持' .. szType .. "类型的节点订阅,跳过此节点。")
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue