update 2026-11-11 05:36:55

This commit is contained in:
action committed 2026-11-11 05:36:55 +08:00
1 parent ff55a8f71a
commit 30afd5f00d
25 files changed
+794 -241

No files matched your search

+3 -3
View File
@@ -6,12 +6,12 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=dae
PKG_VERSION:=2026.11.09
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE:=dae-src-2026.11.09-45df6d7de7ee.tar.gz
PKG_SOURCE:=dae-src-2026.11.09-8b744ffb65ee.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=45df6d7de7ee262cb8b22a377037dc9c1498ffa282b6be4364bee3fbecdbcc55
PKG_HASH:=8b744ffb65eedf46e9f3cf035e757bda30547d2dafed522eb3154bd4e509e146
PKG_LICENSE:=AGPL-3.0-only
PKG_LICENSE_FILE:=LICENSE
+3 -3
View File
@@ -6,12 +6,12 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=daed
PKG_VERSION:=2026.11.09
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE:=daed-src-2026.11.09-82f4f6af67da.tar.gz
PKG_SOURCE:=daed-src-2026.11.09-a25f8d76d474.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=82f4f6af67da0069958810d484e2957a4d8ab48b078d71e27e62d96c5dcd4faa
PKG_HASH:=a25f8d76d4745a114f52f7b80d0636aad7e4a9be06488db8e2753dc119547dd0
PKG_LICENSE:=AGPL-3.0-only MIT
PKG_LICENSE_FILES:=LICENSE wing/LICENSE
+1 -1
View File
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.11.3
PKG_VERSION:=26.11.11
PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION)
@@ -10,6 +10,7 @@ local trojan_type = {}
local vmess_type = {}
local vless_type = {}
local hysteria2_type = {}
local other_type = {}
local xray_version = api.get_app_version("xray")
if has_ss_rust then
local s = "shadowsocks-rust"
@@ -22,6 +23,7 @@ if has_singbox then
table.insert(vmess_type, s)
table.insert(vless_type, s)
table.insert(hysteria2_type, s)
table.insert(other_type, s)
end
if has_xray then
local s = "xray"
@@ -29,6 +31,7 @@ if has_xray then
table.insert(ss_type, s)
table.insert(vmess_type, s)
table.insert(vless_type, s)
table.insert(other_type, s)
if api.compare_versions(xray_version, ">=", "26.1.13") then
table.insert(hysteria2_type, s)
end
@@ -100,7 +103,15 @@ if #hysteria2_type > 0 then
end
end
if #ss_type > 0 or #trojan_type > 0 or #vmess_type > 0 or #vless_type > 0 or #hysteria2_type > 0 then
if #other_type > 0 then
o = s:option(ListValue, "other_type", translatef("%s Node Use Type", translate("Other")))
o:value("", translate("Auto"))
for key, value in pairs(other_type) do
o:value(value)
end
end
if #ss_type > 0 or #trojan_type > 0 or #vmess_type > 0 or #vless_type > 0 or #hysteria2_type > 0 or #other_type > 0 then
o.description = string.format("<font color='red'>%s</font>",
translate("The configured type also applies to the core specified when manually importing nodes."))
end
@@ -23,6 +23,7 @@ local trojan_type = {}
local vmess_type = {}
local vless_type = {}
local hysteria2_type = {}
local other_type = {}
local xray_version = api.get_app_version("xray")
if has_ss_rust then
local s = "shadowsocks-rust"
@@ -35,6 +36,7 @@ if has_singbox then
table.insert(vmess_type, s)
table.insert(vless_type, s)
table.insert(hysteria2_type, s)
table.insert(other_type, s)
end
if has_xray then
local s = "xray"
@@ -42,6 +44,7 @@ if has_xray then
table.insert(ss_type, s)
table.insert(vmess_type, s)
table.insert(vless_type, s)
table.insert(other_type, s)
if api.compare_versions(xray_version, ">=", "26.1.13") then
table.insert(hysteria2_type, s)
end
@@ -222,7 +225,18 @@ if #hysteria2_type > 0 then
for key, value in pairs(hysteria2_type) do
o:value(value)
end
end
if #other_type > 0 then
o = s:option(ListValue, "other_type", translatef("%s Node Use Type", translate("Other")))
o.default = "global"
o:value("global", translate("Use global config"))
for key, value in pairs(other_type) do
o:value(value)
end
end
if #hysteria2_type > 0 then
o = s:option(Value, "hysteria_up_mbps", "Hy/Hy2 " .. translate("Max upload Mbps"))
o.datatype = "uinteger"
@@ -706,6 +706,10 @@ if singbox_tags:find("with_wireguard") then
o = s:option(Value, "wireguard_reserved", translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
o:depends({ protocol = "wireguard" })
o = s:option(Value, "wireguard_keepAlive", translate("Keep Alive"))
o.default = "0"
o:depends({ protocol = "wireguard" })
end
-- [[ TCP部分(模拟) ]]--
@@ -507,6 +507,7 @@ function gen_outbound(flag, node, tag, proxy_table)
pre_shared_key = node.wireguard_preSharedKey,
reserved = node.wireguard_reserved,
mtu = tonumber(node.wireguard_mtu),
persistent_keepalive_interval = tonumber(node.wireguard_keepAlive),
}
end
@@ -2438,7 +2439,8 @@ function gen_config(var)
port = value.server_port,
public_key = value.peer_public_key,
pre_shared_key = value.pre_shared_key,
allowed_ips = {"0.0.0.0/0"},
allowed_ips = {"0.0.0.0/0", "::/0"},
persistent_keepalive_interval = value.persistent_keepalive_interval,
reserved = value.reserved
}
},
+26 -21
View File
@@ -12,7 +12,7 @@ local GLOBAL = {
local xray_version = api.get_app_version("xray")
local xray_min_version = "26.7.11"
local xray_min_version = "26.9.9"
local function get_domain_excluded()
local path = string.format("/etc/%s/rules/domains_excluded", api.c_config)
@@ -355,6 +355,30 @@ function gen_outbound(flag, node, tag, proxy_table)
}
}
udp[#udp+1] = r
elseif node.hysteria2_hop and node.hysteria2_hop ~= "" then
udp[#udp+1] = {
type = "udphop",
settings = {
mode = "intervallocal,intervalremote",
remotePorts = string.gsub(node.hysteria2_hop, ":", "-"),
interval = (function(v)
if not v then return 30 end
if v:find("-", 1, true) then
local min, max = v:match("^(%d+)%-(%d+)$")
min = tonumber(min)
max = tonumber(max)
if min and max then
min = (min >= 5) and min or 5
max = (max >= min) and max or min
return min .. "-" .. max
end
return 30
end
v = tonumber((v or "30"):match("^%d+"))
return (v and v >= 5) and v or 30
end)(node.hysteria2_hop_interval)
}
}
end
finalmask.udp = udp
local up = tonumber(node.hysteria2_up_mbps) or 0
@@ -363,25 +387,6 @@ function gen_outbound(flag, node, tag, proxy_table)
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)
if not v then return 30 end
if v:find("-", 1, true) then
local min, max = v:match("^(%d+)%-(%d+)$")
min = tonumber(min)
max = tonumber(max)
if min and max then
min = (min >= 5) and min or 5
max = (max >= min) and max or min
return min .. "-" .. max
end
return 30
end
v = tonumber((v or "30"):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
@@ -431,7 +436,7 @@ function gen_outbound(flag, node, tag, proxy_table)
peers = (node.protocol == "wireguard") and {
{
publicKey = node.wireguard_public_key,
endpoint = node.address .. ":" .. node.port,
endpoint = (api.is_ipv6(node.address) and ("[" .. node.address .. "]") or node.address) .. ":" .. node.port,
preSharedKey = node.wireguard_preSharedKey,
keepAlive = node.wireguard_keepAlive and tonumber(node.wireguard_keepAlive) or nil
}
@@ -14,6 +14,7 @@ local trojan_type = api.get_core("trojan_type", {{has_singbox,"sing-box"},{has_x
local vmess_type = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local vless_type = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local hysteria2_type = api.get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{has_singbox,"sing-box"}, {has_xray,"xray"}})
local other_type = api.get_core("other_type", {{has_singbox,"sing-box"},{has_xray,"xray"}})
local current_node = map:get(section)
-%>
@@ -27,6 +28,7 @@ local current_node = map:get(section)
let vmess_type = "<%=vmess_type%>"
let vless_type = "<%=vless_type%>"
let hysteria2_type = "<%=hysteria2_type%>"
let other_type = "<%=other_type%>"
function padright(str, cnt, pad) {
return str + Array(cnt + 1).join(pad);
@@ -191,6 +193,8 @@ local current_node = map:get(section)
dom_prefix = "xray_"
} else if (v_type === "sing-box") {
dom_prefix = "singbox_"
} else if (v_type === "Socks") {
dom_prefix = "socks_"
}
var _address = ""
if (dom_prefix && dom_prefix != null) {
@@ -741,6 +745,74 @@ local current_node = map:get(section)
params = params.substring(1);
}
url += params;
} else if ((v_type === "Xray" || v_type === "sing-box") && opt.get(dom_prefix + "protocol").value === "wireguard") {
protocol = "wireguard";
var v_password = opt.get(dom_prefix + "wireguard_secret_key");
var v_port = opt.get(dom_prefix + "port");
url = encodeURIComponent(v_password.value) +
"@" + _address +
":" + v_port.value + "?";
var params = "";
params += opt.query("publickey", dom_prefix + "wireguard_public_key");
params += opt.query("presharedkey", dom_prefix + "wireguard_preSharedKey");
var addresses = opt.getlist(dom_prefix + "wireguard_local_address").map(value => value.trim()).filter(Boolean);
params += "&address=" + encodeURIComponent(addresses.join(","));
params += opt.query("reserved", dom_prefix + "wireguard_reserved");
params += opt.query("mtu", dom_prefix + "wireguard_mtu");
params += opt.query("persistent-keepalive", dom_prefix + "wireguard_keepAlive");
if (v_type === "Xray" && opt.get(dom_prefix + "use_finalmask")?.checked) {
params += opt.query("fm", dom_prefix + "finalmask");
}
params += "#" + encodeURIComponent(v_alias.value);
if (params[0] == "&") {
params = params.substring(1);
}
url += params;
} else if (v_type === "Socks" || ((v_type === "sing-box" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "socks")) {
protocol = "socks";
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 = _address + ":" + v_port.value;
if (v_username.value || v_password.value) {
url = b64encutf8safe(v_username.value + ":" + v_password.value) + "@" + url;
}
var params = "";
params += opt.query("uot", dom_prefix + "uot");
params += opt.query("tfo", dom_prefix + "tcp_fast_open");
if (params) {
url += "?" + params.substring(1);
}
url += "#" + encodeURIComponent(v_alias.value);
} else if ((v_type === "sing-box" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "http") {
protocol = opt.get(dom_prefix + "tls").checked ? "https" : "http";
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 = _address + ":" + v_port.value;
if (v_username.value || v_password.value) {
url = encodeURIComponent(v_username.value) + ":" + encodeURIComponent(v_password.value) + "@" + url;
}
var params = "";
if (protocol === "https") {
params += opt.query("sni", dom_prefix + "tls_serverName");
params += opt.query("alpn", dom_prefix + "alpn");
params += opt.query("insecure", dom_prefix + "tls_allowInsecure");
params += opt.query("pcs", dom_prefix + "tls_pinSHA256");
params += opt.query("vcn", dom_prefix + "tls_CertByName");
if (opt.get(dom_prefix + "utls")?.checked) {
params += opt.query("fp", dom_prefix + "fingerprint");
}
if (opt.get(dom_prefix + "ech")?.checked) {
params += opt.query("ech", dom_prefix + "ech_config");
}
}
params += opt.query("tfo", dom_prefix + "tcp_fast_open");
if (params) {
url += "?" + params.substring(1);
}
url += "#" + encodeURIComponent(v_alias.value);
}
if (url) {
@@ -1828,6 +1900,126 @@ local current_node = map:get(section)
}
}
if (link_type === "wireguard") {
if (other_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_";
opt.set('type', "sing-box");
} else if (other_type == "xray" && has_xray) {
dom_prefix = "xray_";
opt.set('type', "Xray");
}
opt.set(dom_prefix + 'protocol', "wireguard");
var info = link_split[1].split('@');
var m = parseNodeUrl("wireguard://" + info[1]);
var host_port = info[1].split(/[/?#]/)[0];
opt.set(dom_prefix + 'address', unbracketIP(m.hostname));
opt.set(dom_prefix + 'port', host_port.match(/:(\d+)$/)?.[1] || '51820');
opt.set(dom_prefix + 'wireguard_secret_key', decodeURIComponent(info[0]));
var queryParam = {};
if (m.search.length > 1) {
var queryArray = m.search.substr(1).split('&');
for (var i = 0; i < queryArray.length; i++) {
var index = queryArray[i].indexOf('=');
if (index > 0) {
queryParam[decodeURIComponent(queryArray[i].substr(0, index)).toLowerCase()] = decodeURIComponent(queryArray[i].substr(index + 1));
}
}
}
opt.set(dom_prefix + 'wireguard_public_key', queryParam.publickey);
opt.set(dom_prefix + 'wireguard_preSharedKey', queryParam.presharedkey || '');
var addresses = ((queryParam.address || queryParam.ip || '') + ',' + (queryParam.ipv6 || '')).split(/[,\s]+/).filter(Boolean);
var fullName = opt.base + '.' + dom_prefix + 'wireguard_local_address';
var lists = document.querySelectorAll('.cbi-dynlist');
for (var i = 0; i < lists.length; i++) {
var label = lists[i].closest('.cbi-value')?.querySelector('label.cbi-value-title');
if (label?.getAttribute('for') === fullName) {
L.dom.callClassMethod(lists[i], 'setValue', addresses);
lists[i].dispatchEvent(new Event('change', { bubbles: true }));
break;
}
}
opt.set(dom_prefix + 'wireguard_reserved', queryParam.reserved || '');
opt.set(dom_prefix + 'wireguard_mtu', queryParam.mtu || '1420');
opt.set(dom_prefix + 'wireguard_keepAlive', queryParam['persistent-keepalive'] || queryParam.keepalive || '0');
if (dom_prefix === "xray_") {
opt.set(dom_prefix + 'use_finalmask', !!queryParam.fm);
opt.set(dom_prefix + 'finalmask', queryParam.fm || '');
}
if (m.hash) {
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (link_type === "socks" || link_type === "socks5" || link_type === "socks5h" || link_type === "http" || link_type === "https") {
if (other_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_";
opt.set('type', "sing-box");
} else if (other_type == "xray" && has_xray) {
dom_prefix = "xray_";
opt.set('type', "Xray");
}
var protocol = (link_type === "http" || link_type === "https") ? "http" : "socks";
opt.set(dom_prefix + 'protocol', protocol);
var content = link_split[1];
var index = content.search(/[?#]/);
var suffix = index >= 0 ? content.substr(index) : '';
content = index >= 0 ? content.substr(0, index) : content;
var base64_auth = false;
if (protocol === "socks" && content.indexOf('@') === -1 && content.indexOf(':') === -1) {
content = b64decutf8safe(decodeURIComponent(content));
base64_auth = true;
}
var auth = '';
var index = content.lastIndexOf('@');
if (index >= 0) {
auth = content.substr(0, index);
content = content.substr(index + 1);
}
if (protocol === "socks" && auth.indexOf(':') === -1) {
auth = b64decutf8safe(decodeURIComponent(auth));
base64_auth = true;
}
var index = auth.indexOf(':');
var username = index >= 0 ? auth.substr(0, index) : auth;
var password = index >= 0 ? auth.substr(index + 1) : '';
if (!base64_auth) {
username = decodeURIComponent(username);
password = decodeURIComponent(password);
}
var m = new URL(link_type + "://" + content + suffix);
opt.set(dom_prefix + 'address', unbracketIP(m.hostname));
opt.set(dom_prefix + 'port', m.port || (protocol === "socks" ? "1080" : (link_type === "https" ? "443" : "80")));
opt.set(dom_prefix + 'username', username);
opt.set(dom_prefix + 'password', password);
opt.set(dom_prefix + 'transport', dom_prefix === "xray_" ? "raw" : "tcp");
var queryParam = {};
if (m.search.length > 1) {
var queryArray = m.search.substr(1).split('&');
for (var i = 0; i < queryArray.length; i++) {
var index = queryArray[i].indexOf('=');
if (index > 0) {
queryParam[decodeURIComponent(queryArray[i].substr(0, index)).toLowerCase()] = decodeURIComponent(queryArray[i].substr(index + 1));
}
}
}
opt.set(dom_prefix + 'uot', queryParam.uot === '1');
opt.set(dom_prefix + 'tcp_fast_open', queryParam.tfo === '1');
opt.set(dom_prefix + 'tls', link_type === "https");
opt.set(dom_prefix + 'reality', false);
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_allowInsecure', (queryParam.allowinsecure || queryParam.insecure || queryParam.allow_insecure || '0') === '1');
opt.set(dom_prefix + 'tls_pinSHA256', queryParam.pcs || '');
opt.set(dom_prefix + 'tls_CertByName', queryParam.vcn || '');
opt.set(dom_prefix + 'utls', !!queryParam.fp);
opt.set(dom_prefix + 'fingerprint', queryParam.fp || 'chrome');
opt.set(dom_prefix + 'ech', !!queryParam.ech);
opt.set(dom_prefix + 'ech_config', queryParam.ech || '');
if (m.hash) {
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (switch_type) {
sessionStorage.setItem("fromUrl", JSON.stringify({
timestamp: Date.now(),
+6
View File
@@ -1313,12 +1313,18 @@ msgstr "是否允许不安全连接。当勾选时,将跳过证书验证。"
msgid "%s Node Use Type"
msgstr "%s 节点使用类型"
msgid "Other"
msgstr "其他"
msgid "UDP relay mode"
msgstr "UDP 中继模式"
msgid "Congestion control algorithm"
msgstr "拥塞控制算法"
msgid "Keep Alive"
msgstr "保活间隔"
msgid "Heartbeat interval(second)"
msgstr "保活心跳包发送间隔(单位:秒)"
@@ -86,6 +86,7 @@ local DEFAULT_TROJAN_TYPE = api.get_core("trojan_type", {{has_singbox,"sing-box
local DEFAULT_VMESS_TYPE = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local DEFAULT_VLESS_TYPE = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local DEFAULT_HYSTERIA2_TYPE = api.get_core("hysteria2_type", {{has_hysteria2,"hysteria2"},{has_singbox,"sing-box"},{has_xray,"xray"}})
local DEFAULT_OTHER_TYPE = api.get_core("other_type", {{has_singbox,"sing-box"},{has_xray,"xray"}})
local core_has = {
["xray"] = has_xray,
["sing-box"] = has_singbox,
@@ -569,6 +570,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
local sub_vmess_type = DEFAULT_VMESS_TYPE
local sub_vless_type = DEFAULT_VLESS_TYPE
local sub_hysteria2_type = DEFAULT_HYSTERIA2_TYPE
local sub_other_type = DEFAULT_OTHER_TYPE
local sub_hy_up_mbps, sub_hy_down_mbps = 1000, 1000
if sub_cfg then
if sub_cfg.allowInsecure and sub_cfg.allowInsecure ~= "1" then
@@ -594,6 +596,10 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if hysteria2_type ~= "global" and core_has[hysteria2_type] then
sub_hysteria2_type = hysteria2_type
end
local other_type = sub_cfg.other_type or "global"
if other_type ~= "global" and core_has[other_type] then
sub_other_type = other_type
end
sub_hy_up_mbps = sub_cfg.hysteria_up_mbps
sub_hy_down_mbps = sub_cfg.hysteria_down_mbps
end
@@ -1664,6 +1670,115 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.snell_mode = params.mode or "default"
end
result.snell_reuse = (params.reuse == "1") and "1" or "0"
elseif szType == "wireguard" then
if sub_other_type == "sing-box" and has_singbox then
result.type = "sing-box"
elseif sub_other_type == "xray" and has_xray then
result.type = "Xray"
else
log("跳过 WireGuard 节点,因未安装 WireGuard 核心程序 Xray 或 Sing-box。")
return nil
end
result.protocol = "wireguard"
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 contents = split(content, "@")
result.wireguard_secret_key = UrlDecode(contents[1]:gsub("%+", "%%2B"))
local query = split(contents[2], "%?")
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[UrlDecode(v:sub(1, s - 1)):lower()] = UrlDecode(v:sub(s + 1):gsub("%+", "%%2B"))
end
end
result.address, result.port = parse_host_port(host_port, "51820")
result.wireguard_public_key = params.publickey
result.wireguard_preSharedKey = params.presharedkey
result.wireguard_local_address = {}
for value in ((params.address or params.ip or "") .. "," .. (params.ipv6 or "")):gmatch("[^,%s]+") do
result.wireguard_local_address[#result.wireguard_local_address + 1] = value
end
result.wireguard_reserved = params.reserved
result.wireguard_mtu = params.mtu or "1420"
result.wireguard_keepAlive = params["persistent-keepalive"] or params.keepalive or "0"
result.use_finalmask = (params.fm and params.fm ~= "") and "1" or nil
result.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil
elseif szType == "socks" or szType == "socks5" or szType == "socks5h" or szType == "http" or szType == "https" then
if sub_other_type == "sing-box" and has_singbox then
result.type = "sing-box"
elseif sub_other_type == "xray" and has_xray then
result.type = "Xray"
else
log("跳过 " .. szType .. " 节点,因未安装 " .. szType .. " 核心程序 Xray 或 Sing-box。")
return nil
end
result.protocol = (szType == "http" or szType == "https") and "http" or "socks"
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 query = split(content, "%?")
local Info = query[1]
local base64_auth = false
if result.protocol == "socks" and not Info:find("@", 1, true) and not Info:find(":", 1, true) then
Info = base64Decode(UrlDecode(Info:gsub("%+", "%%2B")))
base64_auth = true
end
local auth, host_port = Info:match("^(.*)@(.*)$")
host_port = host_port or Info
auth = auth or ""
if result.protocol == "socks" and not auth:find(":", 1, true) then
auth = base64Decode(UrlDecode(auth:gsub("%+", "%%2B")))
base64_auth = true
end
local idx = auth:find(":", 1, true)
result.username = idx and auth:sub(1, idx - 1) or auth
result.password = idx and auth:sub(idx + 1) or ""
if not base64_auth then
result.username = UrlDecode(result.username:gsub("%+", "%%2B"))
result.password = UrlDecode(result.password:gsub("%+", "%%2B"))
end
local params = {}
for _, v in pairs(split(query[2], '&')) do
local s = v:find("=", 1, true)
if s and s > 1 then
params[UrlDecode(v:sub(1, s - 1)):lower()] = UrlDecode(v:sub(s + 1):gsub("%+", "%%2B"))
end
end
local port = result.protocol == "socks" and "1080" or (szType == "https" and "443" or "80")
result.address, result.port = parse_host_port(host_port, port)
result.transport = result.type == "Xray" and "raw" or "tcp"
result.uot = params.uot
result.tcp_fast_open = params.tfo
result.tls = szType == "https" and "1" or "0"
if result.tls == "1" then
result.tls_serverName = params.sni
result.alpn = params.alpn
local insecure = params.allowinsecure or params.insecure or params.allow_insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.tls_pinSHA256 = params.pcs
result.tls_CertByName = params.vcn
if params.fp and params.fp ~= "" then
result.utls = "1"
result.fingerprint = params.fp
end
if params.ech and params.ech ~= "" then
result.ech = "1"
result.ech_config = params.ech
end
end
else
log("暂时不支持 " .. szType .. " 类型的节点订阅,跳过此节点。")
return nil
+1 -1
View File
@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.11.3
PKG_VERSION:=26.11.11
PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION)
@@ -9,6 +9,7 @@ local trojan_type = {}
local vmess_type = {}
local vless_type = {}
local hysteria2_type = {}
local other_type = {}
local xray_version = api.get_app_version("xray")
if has_ss_rust then
local s = "shadowsocks-rust"
@@ -21,6 +22,7 @@ if has_singbox then
table.insert(vmess_type, s)
table.insert(vless_type, s)
table.insert(hysteria2_type, s)
table.insert(other_type, s)
end
if has_xray then
local s = "xray"
@@ -28,6 +30,7 @@ if has_xray then
table.insert(ss_type, s)
table.insert(vmess_type, s)
table.insert(vless_type, s)
table.insert(other_type, s)
if api.compare_versions(xray_version, ">=", "26.1.13") then
table.insert(hysteria2_type, s)
end
@@ -95,7 +98,15 @@ if #hysteria2_type > 0 then
end
end
if #ss_type > 0 or #trojan_type > 0 or #vmess_type > 0 or #vless_type > 0 or #hysteria2_type > 0 then
if #other_type > 0 then
o = s:option(ListValue, "other_type", translatef("%s Node Use Type", translate("Other")))
o:value("", translate("Auto"))
for key, value in pairs(other_type) do
o:value(value)
end
end
if #ss_type > 0 or #trojan_type > 0 or #vmess_type > 0 or #vless_type > 0 or #hysteria2_type > 0 or #other_type > 0 then
o.description = string.format("<font color='red'>%s</font>",
translate("The configured type also applies to the core specified when manually importing nodes."))
end
@@ -22,6 +22,7 @@ local trojan_type = {}
local vmess_type = {}
local vless_type = {}
local hysteria2_type = {}
local other_type = {}
local xray_version = api.get_app_version("xray")
if has_ss_rust then
local s = "shadowsocks-rust"
@@ -34,6 +35,7 @@ if has_singbox then
table.insert(vmess_type, s)
table.insert(vless_type, s)
table.insert(hysteria2_type, s)
table.insert(other_type, s)
end
if has_xray then
local s = "xray"
@@ -41,6 +43,7 @@ if has_xray then
table.insert(ss_type, s)
table.insert(vmess_type, s)
table.insert(vless_type, s)
table.insert(other_type, s)
if api.compare_versions(xray_version, ">=", "26.1.13") then
table.insert(hysteria2_type, s)
end
@@ -215,7 +218,18 @@ if #hysteria2_type > 0 then
for key, value in pairs(hysteria2_type) do
o:value(value)
end
end
if #other_type > 0 then
o = s:option(ListValue, "other_type", translatef("%s Node Use Type", translate("Other")))
o.default = "global"
o:value("global", translate("Use global config"))
for key, value in pairs(other_type) do
o:value(value)
end
end
if #hysteria2_type > 0 then
o = s:option(Value, "hysteria_up_mbps", "Hy/Hy2 " .. translate("Max upload Mbps"))
o.datatype = "uinteger"
@@ -768,6 +768,10 @@ if singbox_tags:find("with_wireguard") then
o = s:option(Value, "wireguard_reserved", translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
o:depends({ protocol = "wireguard" })
o = s:option(Value, "wireguard_keepAlive", translate("Keep Alive"))
o.default = "0"
o:depends({ protocol = "wireguard" })
end
-- [[ TCP ]]--
@@ -810,16 +814,6 @@ o = s:option(Value, "ws_path", translate("WebSocket Path"))
o.placeholder = "/"
o:depends({ transport = "ws" })
o = s:option(Flag, "ws_enableEarlyData", translate("Enable early data"))
o:depends({ transport = "ws" })
o = s:option(Value, "ws_maxEarlyData", translate("Early data length"))
o.default = "1024"
o:depends({ ws_enableEarlyData = true })
o = s:option(Value, "ws_earlyDataHeaderName", translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol"))
o:depends({ ws_enableEarlyData = true })
-- [[ HTTPUpgrade ]]--
o = s:option(Value, "httpupgrade_host", translate("HTTPUpgrade Host"))
o:depends({ transport = "httpupgrade" })
@@ -459,6 +459,10 @@ o:depends({ transport = "ws" })
o = s:option(Value, "ws_path", translate("WebSocket Path"))
o:depends({ transport = "ws" })
o = s:option(Value, "ws_earlyDataHeaderName", translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol"))
o.placeholder = "Sec-WebSocket-Protocol"
o:depends({ transport = "ws" })
-- [[ HTTPUpgrade ]]--
o = s:option(Value, "httpupgrade_host", translate("HTTPUpgrade Host"))
@@ -366,9 +366,26 @@ function gen_outbound(flag, node, tag, proxy_table)
Host = node.ws_host,
["User-Agent"] = node.user_agent
} or nil,
max_early_data = tonumber(node.ws_maxEarlyData) or nil,
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil -- For compatibility with Xray-core, set it to Sec-WebSocket-Protocol. It needs to be consistent with the server.
}
local path, query = (node.ws_path or "/"):match("^(.-)%?(.*)$")
local params, query_params = {}, {}
for v in (query or ""):gmatch("[^&]+") do
local t = api.split(api.UrlDecode(v), '=')
params[t[1]] = t[2]
if t[1] ~= "ed" and t[1] ~= "eh" then
table.insert(query_params, v)
end
end
local ed = tonumber(params.ed)
if ed then
-- Consume early-data options while preserving the remaining query.
v2ray_transport.path = path
if #query_params > 0 then
v2ray_transport.path = path .. "?" .. table.concat(query_params, "&")
end
v2ray_transport.max_early_data = ed
end
v2ray_transport.early_data_header_name = params.eh or "Sec-WebSocket-Protocol"
end
if node.transport == "httpupgrade" then
@@ -506,6 +523,7 @@ function gen_outbound(flag, node, tag, proxy_table)
pre_shared_key = node.wireguard_preSharedKey,
reserved = node.wireguard_reserved,
mtu = tonumber(node.wireguard_mtu),
persistent_keepalive_interval = tonumber(node.wireguard_keepAlive),
}
end
@@ -822,7 +840,7 @@ function gen_config_server(node)
type = "ws",
path = node.ws_path or "/",
headers = (node.ws_host ~= nil) and { Host = node.ws_host } or nil,
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil -- For compatibility with Xray-core, set it to Sec-WebSocket-Protocol. It needs to be consistent with the server.
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or "Sec-WebSocket-Protocol"
}
end
@@ -2382,7 +2400,8 @@ function gen_config(var)
port = value.server_port,
public_key = value.peer_public_key,
pre_shared_key = value.pre_shared_key,
allowed_ips = {"0.0.0.0/0"},
allowed_ips = {"0.0.0.0/0", "::/0"},
persistent_keepalive_interval = value.persistent_keepalive_interval,
reserved = value.reserved
}
},
@@ -12,7 +12,7 @@ local GLOBAL = {
local xray_version = api.get_app_version("xray")
local xray_min_version = "26.7.11"
local xray_min_version = "26.9.9"
local function get_domain_excluded()
local path = "/usr/share/passwall2/domains_excluded"
@@ -364,6 +364,30 @@ function gen_outbound(flag, node, tag, proxy_table)
}
}
udp[#udp+1] = r
elseif node.hysteria2_hop and node.hysteria2_hop ~= "" then
udp[#udp+1] = {
type = "udphop",
settings = {
mode = "intervallocal,intervalremote",
remotePorts = string.gsub(node.hysteria2_hop, ":", "-"),
interval = (function(v)
if not v then return 30 end
if v:find("-", 1, true) then
local min, max = v:match("^(%d+)%-(%d+)$")
min = tonumber(min)
max = tonumber(max)
if min and max then
min = (min >= 5) and min or 5
max = (max >= min) and max or min
return min .. "-" .. max
end
return 30
end
v = tonumber((v or "30"):match("^%d+"))
return (v and v >= 5) and v or 30
end)(node.hysteria2_hop_interval)
}
}
end
finalmask.udp = udp
local up = tonumber(node.hysteria2_up_mbps) or 0
@@ -372,25 +396,6 @@ function gen_outbound(flag, node, tag, proxy_table)
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)
if not v then return 30 end
if v:find("-", 1, true) then
local min, max = v:match("^(%d+)%-(%d+)$")
min = tonumber(min)
max = tonumber(max)
if min and max then
min = (min >= 5) and min or 5
max = (max >= min) and max or min
return min .. "-" .. max
end
return 30
end
v = tonumber((v or "30"):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
@@ -440,7 +445,7 @@ function gen_outbound(flag, node, tag, proxy_table)
peers = (node.protocol == "wireguard") and {
{
publicKey = node.wireguard_public_key,
endpoint = node.address .. ":" .. node.port,
endpoint = (api.is_ipv6(node.address) and ("[" .. node.address .. "]") or node.address) .. ":" .. node.port,
preSharedKey = node.wireguard_preSharedKey,
keepAlive = node.wireguard_keepAlive and tonumber(node.wireguard_keepAlive) or nil
}
@@ -13,6 +13,7 @@ local trojan_type = api.get_core("trojan_type", {{has_singbox,"sing-box"},{has_x
local vmess_type = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local vless_type = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local hysteria2_type = api.get_core("hysteria2_type", {{has_singbox,"sing-box"}, {has_xray,"xray"}})
local other_type = api.get_core("other_type", {{has_singbox,"sing-box"},{has_xray,"xray"}})
local current_node = map:get(section)
-%>
@@ -25,6 +26,7 @@ local current_node = map:get(section)
let vmess_type = "<%=vmess_type%>"
let vless_type = "<%=vless_type%>"
let hysteria2_type = "<%=hysteria2_type%>"
let other_type = "<%=other_type%>"
function padright(str, cnt, pad) {
return str + Array(cnt + 1).join(pad);
@@ -254,10 +256,6 @@ local current_node = map:get(section)
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 === "http") {
params += opt.query("host", dom_prefix + "http_host");
params += opt.query("path", dom_prefix + "http_path");
@@ -376,10 +374,6 @@ local current_node = map:get(section)
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 === "http") {
info.host = opt.get(dom_prefix + "http_host").value;
info.path = opt.get(dom_prefix + "http_path").value;
@@ -436,10 +430,6 @@ local current_node = map:get(section)
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 += encodeURIComponent("?ed=" + ws_maxEarlyData);
}
} else if (v_transport === "http") {
params += opt.query("host", dom_prefix + "http_host");
params += opt.query("path", dom_prefix + "http_path");
@@ -528,10 +518,6 @@ local current_node = map:get(section)
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 === "http") {
params += opt.query("host", dom_prefix + "http_host");
params += opt.query("path", dom_prefix + "http_path");
@@ -729,6 +715,74 @@ local current_node = map:get(section)
params = params.substring(1);
}
url += params;
} else if ((v_type === "Xray" || v_type === "sing-box") && opt.get(dom_prefix + "protocol").value === "wireguard") {
protocol = "wireguard";
var v_password = opt.get(dom_prefix + "wireguard_secret_key");
var v_port = opt.get(dom_prefix + "port");
url = encodeURIComponent(v_password.value) +
"@" + _address +
":" + v_port.value + "?";
var params = "";
params += opt.query("publickey", dom_prefix + "wireguard_public_key");
params += opt.query("presharedkey", dom_prefix + "wireguard_preSharedKey");
var addresses = opt.getlist(dom_prefix + "wireguard_local_address").map(value => value.trim()).filter(Boolean);
params += "&address=" + encodeURIComponent(addresses.join(","));
params += opt.query("reserved", dom_prefix + "wireguard_reserved");
params += opt.query("mtu", dom_prefix + "wireguard_mtu");
params += opt.query("persistent-keepalive", dom_prefix + "wireguard_keepAlive");
if (v_type === "Xray" && opt.get(dom_prefix + "use_finalmask")?.checked) {
params += opt.query("fm", dom_prefix + "finalmask");
}
params += "#" + encodeURIComponent(v_alias.value);
if (params[0] == "&") {
params = params.substring(1);
}
url += params;
} else if ((v_type === "sing-box" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "socks") {
protocol = "socks";
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 = _address + ":" + v_port.value;
if (v_username.value || v_password.value) {
url = b64encutf8safe(v_username.value + ":" + v_password.value) + "@" + url;
}
var params = "";
params += opt.query("uot", dom_prefix + "uot");
params += opt.query("tfo", dom_prefix + "tcp_fast_open");
if (params) {
url += "?" + params.substring(1);
}
url += "#" + encodeURIComponent(v_alias.value);
} else if ((v_type === "sing-box" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "http") {
protocol = opt.get(dom_prefix + "tls").checked ? "https" : "http";
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 = _address + ":" + v_port.value;
if (v_username.value || v_password.value) {
url = encodeURIComponent(v_username.value) + ":" + encodeURIComponent(v_password.value) + "@" + url;
}
var params = "";
if (protocol === "https") {
params += opt.query("sni", dom_prefix + "tls_serverName");
params += opt.query("alpn", dom_prefix + "alpn");
params += opt.query("insecure", dom_prefix + "tls_allowInsecure");
params += opt.query("pcs", dom_prefix + "tls_pinSHA256");
params += opt.query("vcn", dom_prefix + "tls_CertByName");
if (opt.get(dom_prefix + "utls")?.checked) {
params += opt.query("fp", dom_prefix + "fingerprint");
}
if (opt.get(dom_prefix + "ech")?.checked) {
params += opt.query("ech", dom_prefix + "ech_config");
}
}
params += opt.query("tfo", dom_prefix + "tcp_fast_open");
if (params) {
url += "?" + params.substring(1);
}
url += "#" + encodeURIComponent(v_alias.value);
}
if (url) {
@@ -1111,24 +1165,6 @@ local current_node = map:get(section)
} 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 === "http") {
if (dom_prefix == "xray_") {
opt.set(dom_prefix + 'xhttp_mode', "stream-one");
@@ -1259,24 +1295,6 @@ local current_node = map:get(section)
} 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") {
if (dom_prefix == "xray_") {
opt.set(dom_prefix + 'xhttp_mode', "stream-one");
@@ -1446,24 +1464,6 @@ local current_node = map:get(section)
} 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 === "http") {
if (dom_prefix == "xray_") {
opt.set(dom_prefix + 'xhttp_mode', "stream-one");
@@ -1589,24 +1589,6 @@ local current_node = map:get(section)
} 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") {
if (dom_prefix == "xray_") {
opt.set(dom_prefix + 'xhttp_mode', "stream-one");
@@ -1874,6 +1856,126 @@ local current_node = map:get(section)
}
}
if (link_type === "wireguard") {
if (other_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_";
opt.set('type', "sing-box");
} else if (other_type == "xray" && has_xray) {
dom_prefix = "xray_";
opt.set('type', "Xray");
}
opt.set(dom_prefix + 'protocol', "wireguard");
var info = link_split[1].split('@');
var m = parseNodeUrl("wireguard://" + info[1]);
var host_port = info[1].split(/[/?#]/)[0];
opt.set(dom_prefix + 'address', unbracketIP(m.hostname));
opt.set(dom_prefix + 'port', host_port.match(/:(\d+)$/)?.[1] || '51820');
opt.set(dom_prefix + 'wireguard_secret_key', decodeURIComponent(info[0]));
var queryParam = {};
if (m.search.length > 1) {
var queryArray = m.search.substr(1).split('&');
for (var i = 0; i < queryArray.length; i++) {
var index = queryArray[i].indexOf('=');
if (index > 0) {
queryParam[decodeURIComponent(queryArray[i].substr(0, index)).toLowerCase()] = decodeURIComponent(queryArray[i].substr(index + 1));
}
}
}
opt.set(dom_prefix + 'wireguard_public_key', queryParam.publickey);
opt.set(dom_prefix + 'wireguard_preSharedKey', queryParam.presharedkey || '');
var addresses = ((queryParam.address || queryParam.ip || '') + ',' + (queryParam.ipv6 || '')).split(/[,\s]+/).filter(Boolean);
var fullName = opt.base + '.' + dom_prefix + 'wireguard_local_address';
var lists = document.querySelectorAll('.cbi-dynlist');
for (var i = 0; i < lists.length; i++) {
var label = lists[i].closest('.cbi-value')?.querySelector('label.cbi-value-title');
if (label?.getAttribute('for') === fullName) {
L.dom.callClassMethod(lists[i], 'setValue', addresses);
lists[i].dispatchEvent(new Event('change', { bubbles: true }));
break;
}
}
opt.set(dom_prefix + 'wireguard_reserved', queryParam.reserved || '');
opt.set(dom_prefix + 'wireguard_mtu', queryParam.mtu || '1420');
opt.set(dom_prefix + 'wireguard_keepAlive', queryParam['persistent-keepalive'] || queryParam.keepalive || '0');
if (dom_prefix === "xray_") {
opt.set(dom_prefix + 'use_finalmask', !!queryParam.fm);
opt.set(dom_prefix + 'finalmask', queryParam.fm || '');
}
if (m.hash) {
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (link_type === "socks" || link_type === "socks5" || link_type === "socks5h" || link_type === "http" || link_type === "https") {
if (other_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_";
opt.set('type', "sing-box");
} else if (other_type == "xray" && has_xray) {
dom_prefix = "xray_";
opt.set('type', "Xray");
}
var protocol = (link_type === "http" || link_type === "https") ? "http" : "socks";
opt.set(dom_prefix + 'protocol', protocol);
var content = link_split[1];
var index = content.search(/[?#]/);
var suffix = index >= 0 ? content.substr(index) : '';
content = index >= 0 ? content.substr(0, index) : content;
var base64_auth = false;
if (protocol === "socks" && content.indexOf('@') === -1 && content.indexOf(':') === -1) {
content = b64decutf8safe(decodeURIComponent(content));
base64_auth = true;
}
var auth = '';
var index = content.lastIndexOf('@');
if (index >= 0) {
auth = content.substr(0, index);
content = content.substr(index + 1);
}
if (protocol === "socks" && auth.indexOf(':') === -1) {
auth = b64decutf8safe(decodeURIComponent(auth));
base64_auth = true;
}
var index = auth.indexOf(':');
var username = index >= 0 ? auth.substr(0, index) : auth;
var password = index >= 0 ? auth.substr(index + 1) : '';
if (!base64_auth) {
username = decodeURIComponent(username);
password = decodeURIComponent(password);
}
var m = new URL(link_type + "://" + content + suffix);
opt.set(dom_prefix + 'address', unbracketIP(m.hostname));
opt.set(dom_prefix + 'port', m.port || (protocol === "socks" ? "1080" : (link_type === "https" ? "443" : "80")));
opt.set(dom_prefix + 'username', username);
opt.set(dom_prefix + 'password', password);
opt.set(dom_prefix + 'transport', dom_prefix === "xray_" ? "raw" : "tcp");
var queryParam = {};
if (m.search.length > 1) {
var queryArray = m.search.substr(1).split('&');
for (var i = 0; i < queryArray.length; i++) {
var index = queryArray[i].indexOf('=');
if (index > 0) {
queryParam[decodeURIComponent(queryArray[i].substr(0, index)).toLowerCase()] = decodeURIComponent(queryArray[i].substr(index + 1));
}
}
}
opt.set(dom_prefix + 'uot', queryParam.uot === '1');
opt.set(dom_prefix + 'tcp_fast_open', queryParam.tfo === '1');
opt.set(dom_prefix + 'tls', link_type === "https");
opt.set(dom_prefix + 'reality', false);
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_allowInsecure', (queryParam.allowinsecure || queryParam.insecure || queryParam.allow_insecure || '0') === '1');
opt.set(dom_prefix + 'tls_pinSHA256', queryParam.pcs || '');
opt.set(dom_prefix + 'tls_CertByName', queryParam.vcn || '');
opt.set(dom_prefix + 'utls', !!queryParam.fp);
opt.set(dom_prefix + 'fingerprint', queryParam.fp || 'chrome');
opt.set(dom_prefix + 'ech', !!queryParam.ech);
opt.set(dom_prefix + 'ech_config', queryParam.ech || '');
if (m.hash) {
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (switch_type) {
sessionStorage.setItem("fromUrl", JSON.stringify({
timestamp: Date.now(),
+6 -6
View File
@@ -604,6 +604,9 @@ msgstr "پنجره دریافت اتصال QUIC"
msgid "QUIC concurrent bidirectional streams"
msgstr "جریان‌های دوطرفه همزمان QUIC"
msgid "Keep Alive"
msgstr "زنده نگه داشتن اتصال"
msgid "QUIC KeepAlive interval"
msgstr "فاصله QUIC KeepAlive"
@@ -1369,6 +1372,9 @@ msgstr "زمانی استفاده می‌شود که لینک گره شامل ا
msgid "%s Node Use Type"
msgstr "نوع استفاده گره %s"
msgid "Other"
msgstr "سایر"
msgid "UDP relay mode"
msgstr "حالت بازپخش UDP"
@@ -1585,12 +1591,6 @@ msgstr "همزمانی XUDP Mux"
msgid "Padding"
msgstr "پدینگ (Padding)"
msgid "Enable early data"
msgstr "فعال‌سازی داده‌های اولیه (Early Data)"
msgid "Early data length"
msgstr "طول داده‌های اولیه"
msgid "Early data header name"
msgstr "نام هدر داده‌های اولیه"
+6 -6
View File
@@ -605,6 +605,9 @@ msgstr "Размер окна приёма соединения QUIC"
msgid "QUIC concurrent bidirectional streams"
msgstr "Макс. количество одновременных двунаправленных потоков QUIC"
msgid "Keep Alive"
msgstr "Поддержание соединения"
msgid "QUIC KeepAlive interval"
msgstr "Интервал QUIC KeepAlive"
@@ -1370,6 +1373,9 @@ msgstr "Используется, если в ссылке на узел это
msgid "%s Node Use Type"
msgstr "Тип использования сервера %s"
msgid "Other"
msgstr "Другие"
msgid "UDP relay mode"
msgstr "Режим ретрансляции UDP"
@@ -1586,12 +1592,6 @@ msgstr "Макс. параллельных соединений XUDP"
msgid "Padding"
msgstr "Заполнение"
msgid "Enable early data"
msgstr "Включить 0-RTT данные"
msgid "Early data length"
msgstr "Макс. длина 0-RTT данных"
msgid "Early data header name"
msgstr "Имя HTTP-заголовка для 0-RTT"
+6 -6
View File
@@ -596,6 +596,9 @@ msgstr "QUIC 连接接收窗口"
msgid "QUIC concurrent bidirectional streams"
msgstr "QUIC 并发双向流的最大数量"
msgid "Keep Alive"
msgstr "保持连接"
msgid "QUIC KeepAlive interval"
msgstr "QUIC KeepAlive 间隔"
@@ -1361,6 +1364,9 @@ msgstr "当节点链接未包含该参数时,将使用此设置。"
msgid "%s Node Use Type"
msgstr "%s 节点使用类型"
msgid "Other"
msgstr "其他"
msgid "UDP relay mode"
msgstr "UDP中继模式"
@@ -1577,12 +1583,6 @@ msgstr "XUDP 最大并发连接数"
msgid "Padding"
msgstr "填充"
msgid "Enable early data"
msgstr "启用前置数据"
msgid "Early data length"
msgstr "前置数据最大长度"
msgid "Early data header name"
msgstr "前置数据 HTTP 头名"
+6 -6
View File
@@ -596,6 +596,9 @@ msgstr "QUIC 連接接收窗口"
msgid "QUIC concurrent bidirectional streams"
msgstr "QUIC 並發双向流的最大數量"
msgid "Keep Alive"
msgstr "保持連線"
msgid "QUIC KeepAlive interval"
msgstr "QUIC KeepAlive 間隔"
@@ -1361,6 +1364,9 @@ msgstr "當節點連結未包含該參數時,將使用此設定。"
msgid "%s Node Use Type"
msgstr "%s 節點使用類型"
msgid "Other"
msgstr "其他"
msgid "UDP relay mode"
msgstr "UDP中继模式"
@@ -1577,12 +1583,6 @@ msgstr "XUDP 最大並發連接數"
msgid "Padding"
msgstr "填充"
msgid "Enable early data"
msgstr "啟用前置數据"
msgid "Early data length"
msgstr "前置數据最大长度"
msgid "Early data header name"
msgstr "前置數据 HTTP 头名"
@@ -100,6 +100,7 @@ local DEFAULT_TROJAN_TYPE = api.get_core("trojan_type", {{has_singbox,"sing-box"
local DEFAULT_VMESS_TYPE = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local DEFAULT_VLESS_TYPE = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local DEFAULT_HYSTERIA2_TYPE = api.get_core("hysteria2_type", {{has_singbox,"sing-box"},{has_xray,"xray"}})
local DEFAULT_OTHER_TYPE = api.get_core("other_type", {{has_singbox,"sing-box"},{has_xray,"xray"}})
local core_has = {
["xray"] = has_xray,
["sing-box"] = has_singbox,
@@ -536,6 +537,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
local sub_vmess_type = DEFAULT_VMESS_TYPE
local sub_vless_type = DEFAULT_VLESS_TYPE
local sub_hysteria2_type = DEFAULT_HYSTERIA2_TYPE
local sub_other_type = DEFAULT_OTHER_TYPE
local sub_hy_up_mbps, sub_hy_down_mbps
if sub_cfg then
if sub_cfg.allowInsecure and sub_cfg.allowInsecure ~= "1" then
@@ -561,6 +563,10 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if hysteria2_type ~= "global" and core_has[hysteria2_type] then
sub_hysteria2_type = hysteria2_type
end
local other_type = sub_cfg.other_type or "global"
if other_type ~= "global" and core_has[other_type] then
sub_other_type = other_type
end
sub_hy_up_mbps = sub_cfg.hysteria_up_mbps
sub_hy_down_mbps = sub_cfg.hysteria_down_mbps
end
@@ -679,21 +685,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
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 == "http" then
if result.type == "Xray" then
@@ -936,21 +927,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
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 == "http" then
if result.type == "sing-box" then
@@ -1152,21 +1128,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
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 == "http" then
if result.type == "sing-box" then
@@ -1281,21 +1242,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
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 == "http" then
if result.type == "sing-box" then
@@ -1682,6 +1628,115 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.snell_mode = params.mode or "default"
end
result.snell_reuse = (params.reuse == "1") and "1" or "0"
elseif szType == "wireguard" then
if sub_other_type == "sing-box" and has_singbox then
result.type = "sing-box"
elseif sub_other_type == "xray" and has_xray then
result.type = "Xray"
else
log(2, i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "WireGuard", "Xray / sing-box"))
return nil
end
result.protocol = "wireguard"
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 contents = split(content, "@")
result.wireguard_secret_key = UrlDecode(contents[1]:gsub("%+", "%%2B"))
local query = split(contents[2], "%?")
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[UrlDecode(v:sub(1, s - 1)):lower()] = UrlDecode(v:sub(s + 1):gsub("%+", "%%2B"))
end
end
result.address, result.port = parse_host_port(host_port, "51820")
result.wireguard_public_key = params.publickey
result.wireguard_preSharedKey = params.presharedkey
result.wireguard_local_address = {}
for value in ((params.address or params.ip or "") .. "," .. (params.ipv6 or "")):gmatch("[^,%s]+") do
result.wireguard_local_address[#result.wireguard_local_address + 1] = value
end
result.wireguard_reserved = params.reserved
result.wireguard_mtu = params.mtu or "1420"
result.wireguard_keepAlive = params["persistent-keepalive"] or params.keepalive or "0"
result.use_finalmask = (params.fm and params.fm ~= "") and "1" or nil
result.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil
elseif szType == "socks" or szType == "socks5" or szType == "socks5h" or szType == "http" or szType == "https" then
if sub_other_type == "sing-box" and has_singbox then
result.type = "sing-box"
elseif sub_other_type == "xray" and has_xray then
result.type = "Xray"
else
log(2, i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", szType, "Xray / sing-box"))
return nil
end
result.protocol = (szType == "http" or szType == "https") and "http" or "socks"
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 query = split(content, "%?")
local Info = query[1]
local base64_auth = false
if result.protocol == "socks" and not Info:find("@", 1, true) and not Info:find(":", 1, true) then
Info = base64Decode(UrlDecode(Info:gsub("%+", "%%2B")))
base64_auth = true
end
local auth, host_port = Info:match("^(.*)@(.*)$")
host_port = host_port or Info
auth = auth or ""
if result.protocol == "socks" and not auth:find(":", 1, true) then
auth = base64Decode(UrlDecode(auth:gsub("%+", "%%2B")))
base64_auth = true
end
local idx = auth:find(":", 1, true)
result.username = idx and auth:sub(1, idx - 1) or auth
result.password = idx and auth:sub(idx + 1) or ""
if not base64_auth then
result.username = UrlDecode(result.username:gsub("%+", "%%2B"))
result.password = UrlDecode(result.password:gsub("%+", "%%2B"))
end
local params = {}
for _, v in pairs(split(query[2], '&')) do
local s = v:find("=", 1, true)
if s and s > 1 then
params[UrlDecode(v:sub(1, s - 1)):lower()] = UrlDecode(v:sub(s + 1):gsub("%+", "%%2B"))
end
end
local port = result.protocol == "socks" and "1080" or (szType == "https" and "443" or "80")
result.address, result.port = parse_host_port(host_port, port)
result.transport = result.type == "Xray" and "raw" or "tcp"
result.uot = params.uot
result.tcp_fast_open = params.tfo
result.tls = szType == "https" and "1" or "0"
if result.tls == "1" then
result.tls_serverName = params.sni
result.alpn = params.alpn
local insecure = params.allowinsecure or params.insecure or params.allow_insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.tls_pinSHA256 = params.pcs
result.tls_CertByName = params.vcn
if params.fp and params.fp ~= "" then
result.utls = "1"
result.fingerprint = params.fp
end
if params.ech and params.ech ~= "" then
result.ech = "1"
result.ech_config = params.ech
end
end
else
log(2, i18n.translatef("%s type node subscriptions are not currently supported, skip this node.", szType))
return nil
+2 -2
View File
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=eedb24b3b9fcba27e06b92c0428dab90d9496083091ae4cf39e11387fd99d0e2
endef
GEOSITE_VER:=20261110004853
GEOSITE_VER:=20261110034535
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
HASH:=5ce9f05c8968772940e29b7898adf232464a2a2a820252d17c5b33ffaecd1da9
HASH:=6cc4decf520dbfd96e1e3750661976eb31d2a5381090b8768cb2ab1528201fcf
endef
GEOSITE_IRAN_VER:=202610230802