From 30afd5f00da10cad9f0d6cbf5243d86a609f68fa Mon Sep 17 00:00:00 2001 From: action Date: Wed, 11 Nov 2026 05:36:55 +0800 Subject: [PATCH] update 2026-11-11 05:36:55 --- dae/Makefile | 6 +- daed/Makefile | 6 +- luci-app-passwall/Makefile | 2 +- .../cbi/passwall/client/node_subscribe.lua | 13 +- .../passwall/client/node_subscribe_config.lua | 14 + .../cbi/passwall/client/type/1_sing-box.lua | 4 + .../luasrc/passwall/util_sing-box.lua | 4 +- .../luasrc/passwall/util_xray.lua | 47 +-- .../passwall/node_config/link_share_man.htm | 192 ++++++++++++ luci-app-passwall/po/zh-cn/passwall.po | 6 + .../root/usr/share/passwall/subscribe.lua | 115 ++++++++ luci-app-passwall2/Makefile | 2 +- .../cbi/passwall2/client/node_subscribe.lua | 13 +- .../client/node_subscribe_config.lua | 14 + .../cbi/passwall2/client/type/1_sing-box.lua | 14 +- .../cbi/passwall2/server/type/1_sing-box.lua | 4 + .../luasrc/passwall2/util_sing-box.lua | 27 +- .../luasrc/passwall2/util_xray.lua | 47 +-- .../passwall2/node_config/link_share_man.htm | 278 ++++++++++++------ luci-app-passwall2/po/fa/passwall2.po | 12 +- luci-app-passwall2/po/ru/passwall2.po | 12 +- luci-app-passwall2/po/zh-cn/passwall2.po | 12 +- luci-app-passwall2/po/zh-tw/passwall2.po | 12 +- .../root/usr/share/passwall2/subscribe.lua | 175 +++++++---- v2ray-geodata/Makefile | 4 +- 25 files changed, 794 insertions(+), 241 deletions(-) diff --git a/dae/Makefile b/dae/Makefile index a413a6c9..a1b77a3f 100644 --- a/dae/Makefile +++ b/dae/Makefile @@ -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 diff --git a/daed/Makefile b/daed/Makefile index 7ac84429..7e7971be 100644 --- a/daed/Makefile +++ b/daed/Makefile @@ -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 diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index 0a0d6b22..0cf145e9 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -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) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe.lua index 8762ace1..4cb05527 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe.lua @@ -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("%s", translate("The configured type also applies to the core specified when manually importing nodes.")) end diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua index f504e826..531349f5 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua @@ -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" diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/1_sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/1_sing-box.lua index c36f3073..5b9d08d8 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/1_sing-box.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/1_sing-box.lua @@ -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部分(模拟) ]]-- diff --git a/luci-app-passwall/luasrc/passwall/util_sing-box.lua b/luci-app-passwall/luasrc/passwall/util_sing-box.lua index 90a6df4f..075b2c3b 100644 --- a/luci-app-passwall/luasrc/passwall/util_sing-box.lua +++ b/luci-app-passwall/luasrc/passwall/util_sing-box.lua @@ -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 } }, diff --git a/luci-app-passwall/luasrc/passwall/util_xray.lua b/luci-app-passwall/luasrc/passwall/util_xray.lua index 50af4178..5cf6a452 100644 --- a/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -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 } diff --git a/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm b/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm index 51513554..6918a98f 100644 --- a/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm +++ b/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm @@ -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(), diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po index 1cf806af..56045219 100644 --- a/luci-app-passwall/po/zh-cn/passwall.po +++ b/luci-app-passwall/po/zh-cn/passwall.po @@ -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 "保活心跳包发送间隔(单位:秒)" diff --git a/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/luci-app-passwall/root/usr/share/passwall/subscribe.lua index 356d6f23..1464e4da 100755 --- a/luci-app-passwall/root/usr/share/passwall/subscribe.lua +++ b/luci-app-passwall/root/usr/share/passwall/subscribe.lua @@ -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 diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index 57b72e6c..4373278c 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 -PKG_VERSION:=26.11.3 +PKG_VERSION:=26.11.11 PKG_RELEASE:=1 PKG_PO_VERSION:=$(PKG_VERSION) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua index 4b09a522..662b06e8 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua @@ -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("%s", translate("The configured type also applies to the core specified when manually importing nodes.")) end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua index 36407bdc..2d5f594b 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua @@ -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" diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/1_sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/1_sing-box.lua index 5a9587fb..fe73e27f 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/1_sing-box.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/1_sing-box.lua @@ -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" }) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/1_sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/1_sing-box.lua index 2f6c9481..8815f57d 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/1_sing-box.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/1_sing-box.lua @@ -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")) diff --git a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua index a73d8949..c5e030e0 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua @@ -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 } }, diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua index 1a03a216..01c7f568 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua @@ -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 } diff --git a/luci-app-passwall2/luasrc/view/passwall2/node_config/link_share_man.htm b/luci-app-passwall2/luasrc/view/passwall2/node_config/link_share_man.htm index 7741cca5..ba9555dd 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/node_config/link_share_man.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/node_config/link_share_man.htm @@ -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(), diff --git a/luci-app-passwall2/po/fa/passwall2.po b/luci-app-passwall2/po/fa/passwall2.po index 2f264474..2b9994f1 100644 --- a/luci-app-passwall2/po/fa/passwall2.po +++ b/luci-app-passwall2/po/fa/passwall2.po @@ -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 "نام هدر داده‌های اولیه" diff --git a/luci-app-passwall2/po/ru/passwall2.po b/luci-app-passwall2/po/ru/passwall2.po index 700bedd5..97b3487f 100644 --- a/luci-app-passwall2/po/ru/passwall2.po +++ b/luci-app-passwall2/po/ru/passwall2.po @@ -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" diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po index 0017efa4..9dff3ec0 100644 --- a/luci-app-passwall2/po/zh-cn/passwall2.po +++ b/luci-app-passwall2/po/zh-cn/passwall2.po @@ -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 头名" diff --git a/luci-app-passwall2/po/zh-tw/passwall2.po b/luci-app-passwall2/po/zh-tw/passwall2.po index 1d993c71..a0c9488b 100644 --- a/luci-app-passwall2/po/zh-tw/passwall2.po +++ b/luci-app-passwall2/po/zh-tw/passwall2.po @@ -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 头名" diff --git a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua index 69ec808a..fdf27e82 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua +++ b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua @@ -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 diff --git a/v2ray-geodata/Makefile b/v2ray-geodata/Makefile index 692d6d32..7ecba74a 100644 --- a/v2ray-geodata/Makefile +++ b/v2ray-geodata/Makefile @@ -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