🍓 Sync 2026-10-01 00:51:52

This commit is contained in:
github-actions[bot] committed 2026-10-01 00:51:52 +08:00
1 parent 57dc97412f
commit d45a1cb4ad
60 files changed
+2630 -1193

No files matched your search

+2 -2
View File
@@ -6,8 +6,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.9.16
PKG_RELEASE:=131
PKG_VERSION:=26.10.1
PKG_RELEASE:=132
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
@@ -271,11 +271,12 @@ o:value("proxy", translate("Proxy"))
o = s:option(Value, "user_agent", translate("User-Agent"))
o.default = "v2rayN/9.99"
o:value("curl", "Curl")
o:value("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Linux")
o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Windows")
o:value("passwall2", "PassWall2")
o:value("v2rayN/9.99", "v2rayN")
o:value("clash.meta/v9.99.99", "Clash.Meta")
o:value("Shadowrocket/9999 CFNetwork/3896.100.1.2.1 Darwin/27.0.0 iPhone19,7", "Shadowrocket")
o:value("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Linux")
o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Windows")
o = s:option(ListValue, "chain_proxy", translate("Chain Proxy"))
o:value("", translate("Close(Not use)"))
@@ -560,10 +560,18 @@ o:depends({ protocol = "naive" })
o = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
o.default = "0"
o:depends({ tls = true })
o:depends({ protocol = "hysteria"})
o:depends({ protocol = "hysteria" })
o:depends({ protocol = "tuic" })
o:depends({ protocol = "hysteria2" })
o = s:option(Value, "tls_pinSHA256", translate("TLS Chain Fingerprint (SHA256)"))
o:depends({ tls = true })
o:depends({ protocol = "hysteria" })
o:depends({ protocol = "tuic" })
o:depends({ protocol = "hysteria2" })
o.description = translate("Once set, connects only when the server’s chain fingerprint matches.") ..
string.format("<a href='javascript:void(0)' onclick='javascript:fetchCertSha256(this)'>%s</a>", "→ " .. translate("Fetch Manually"))
o = s:option(Flag, "tls_certificate", translate("TLS Certificate (PEM)"))
o.default = "0"
o:depends({ tls = true, reality = false })
+31 -9
View File
@@ -227,16 +227,19 @@ function exec_call(cmd)
end
function base64Decode(text)
if not text then return '' end
local encoded = text:gsub("%z", ""):gsub("%c", ""):gsub("_", "/"):gsub("-", "+")
if type(text) ~= "string" then return "" end
local encoded = text:gsub("%z", ""):gsub("%c", ""):gsub("_", "/"):gsub("-", "+"):gsub("=+$", "")
if encoded == "" then return text end
if not encoded:match("^[A-Za-z0-9+/]*$") then return text end
local mod4 = #encoded % 4
encoded = encoded .. string.sub('====', mod4 + 1)
local result = nixio.bin.b64decode(encoded)
if result then
return result:gsub("%z", "")
else
return text
end
if mod4 == 1 then return text end
local padded = encoded .. string.rep("=", (4 - mod4) % 4)
local result = nixio.bin.b64decode(padded)
if not result then return text end
-- Verify that the normalized input is canonical Base64.
local reencoded = nixio.bin.b64encode(result):gsub("=+$", "")
if reencoded ~= encoded then return text end
return (result:gsub("%z", ""))
end
function base64Encode(text)
@@ -1866,6 +1869,25 @@ function fetch_cert_sha256(host, port, sni, timeout)
return fp:upper()
end
function sha256_xray_sb(str)
local decoded = base64Decode(str)
if decoded ~= str and #decoded == 32 then return str end
local hex = str:gsub(":", "")
if #hex ~= 64 or not hex:match("^[A-Fa-f0-9]+$") then return str end
local binary = hex:gsub("%x%x", function(byte)
return string.char(tonumber(byte, 16))
end)
return base64Encode(binary)
end
function sha256_sb_xray(str)
local binary = base64Decode(str)
if binary == str or #binary ~= 32 then return str end
return (binary:gsub(".", function(byte)
return string.format("%02X", string.byte(byte))
end))
end
function get_dnsmasq_server_domain()
local dnsmasq_server = uci:get("dhcp", "@dnsmasq[0]", "server")
local dnsmasq_server_t = {}
@@ -9,6 +9,7 @@ local ech_domain = {}
local local_version = api.get_app_version("sing-box"):match("[^v]+")
local version_ge_1_14_0 = api.compare_versions(local_version, ">=", "1.14.0")
local version_ge_1_15_0 = api.compare_versions(local_version, ">=", "1.15.0")
local GLOBAL = {
DNS_SERVER = {}
@@ -248,7 +249,7 @@ function gen_outbound(flag, node, tag, proxy_table)
enabled = true,
disable_sni = (node.tls_disable_sni == "1") and true or false, -- Do not send the server name in ClientHello.
server_name = node.tls_serverName, -- Used to verify the hostname on the returned certificate, unless the settings are insecure. It is also included in ClientHello to support virtual hosts, unless it is an IP address.
insecure = node.tls_allowInsecure == "1" or (node.tls_pinSHA256 and node.tls_pinSHA256 ~= ""), -- Accepts any server certificate. (Also compatible with xray's pinnedPeerCertSha256)
insecure = node.tls_allowInsecure == "1", -- Accepts any server certificate.
alpn = alpn, -- A list of supported application layer protocols, arranged in order of priority. If both peers support ALPN, the protocol selected will be one of these protocols; otherwise, the connection will fail.
--max_version = "1.3",
fragment = fragment,
@@ -294,6 +295,13 @@ function gen_outbound(flag, node, tag, proxy_table)
short_id = node.reality_shortId
} or nil
}
if version_ge_1_15_0 then
if node.tls_pinSHA256 and node.tls_pinSHA256 ~= "" then
tls.certificate_sha256 = { api.sha256_xray_sb(node.tls_pinSHA256) }
end
else
tls.insecure = node.tls_allowInsecure == "1" or (node.tls_pinSHA256 and node.tls_pinSHA256 ~= "")
end
end
local mux = nil
@@ -1488,6 +1496,10 @@ function gen_config(var)
end
end
if to_node then
local chained_tag = outbound.tag .. " -> " .. to_node[".name"] .. (to_node.remarks and ":" .. to_node.remarks or "")
for _, o in ipairs(outbounds_table) do
if o.tag == chained_tag then return chained_tag, last_insert_outbound end
end
local to_outbound
if to_node.type ~= "sing-box" then
local tag = to_node[".name"]
@@ -1549,7 +1561,7 @@ function gen_config(var)
local outbound, exist
if node.protocol == "_urltest" then
outbound, exist = gen_urltest_outbound(node)
if exist then
if exist and not node.chain_proxy then
return outbound.tag
end
elseif node.protocol == "_iface" then
@@ -1583,7 +1595,7 @@ function gen_config(var)
end
if outbound then
local default_outbound_tag, last_insert_outbound = set_outbound_detour(node, outbound, outbounds)
table.insert(outbounds, outbound)
if not exist then table.insert(outbounds, outbound) end
if last_insert_outbound then
table.insert(outbounds, last_insert_outbound)
end
@@ -2030,7 +2042,7 @@ function gen_config(var)
tag = fakedns_tag,
type = "fakeip",
inet4_range = "198.18.0.0/16",
inet6_range = "fc00::/18",
inet6_range = "2001:2::/48",
})
if not experimental then
@@ -186,7 +186,7 @@ function gen_outbound(flag, node, tag, proxy_table)
tlsSettings = (node.stream_security == "tls") and {
serverName = node.tls_serverName,
fingerprint = (node.type == "Xray" and node.utls == "1" and node.fingerprint and node.fingerprint ~= "") and node.fingerprint or nil,
pinnedPeerCertSha256 = node.tls_pinSHA256 or "",
pinnedPeerCertSha256 = (node.tls_pinSHA256 and node.tls_pinSHA256 ~= "") and api.sha256_sb_xray(node.tls_pinSHA256) or "",
verifyPeerCertByName = node.tls_CertByName or "",
echConfigList = (node.ech == "1") and node.ech_config or nil,
certificates = (node.tls_certificate == "1" and node.tls_certificate_pem ~= "") and {
@@ -1058,6 +1058,9 @@ function gen_config(var)
function gen_loopback(outbound_tag, loopback_dst)
if not outbound_tag or outbound_tag == "" then return nil end
local inbound_tag = loopback_dst and "lo-to-" .. loopback_dst or outbound_tag .. "-lo"
for _, o in ipairs(outbounds) do
if o.tag == outbound_tag and o.protocol == "loopback" and o.settings.inboundTag == inbound_tag then return o end
end
local loopback_outbound = {
protocol = "loopback",
tag = outbound_tag,
@@ -1697,7 +1700,7 @@ function gen_config(var)
poolSize = 65535
}
local fakedns6 = {
ipPool = "fc00::/18",
ipPool = "2001:2::/48",
poolSize = 65535
}
if remote_dns_query_strategy == "UseIP" then
@@ -22,7 +22,7 @@ local sid = self.section
alert(res && res.msg ? res.msg : "<%:Fetch Failed%> !");
return;
}
const inputs = document.querySelectorAll('input[id$=".xray_tls_pinSHA256"]');
const inputs = document.querySelectorAll('input[id$="_tls_pinSHA256"]');
inputs.forEach(function(input) {
input.value = res.data;
});
@@ -41,19 +41,25 @@ local current_node = map:get(section)
}
function b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
try {
return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
} catch (e) {}
return str;
}
function b64decutf8safe(str) {
var l;
const orig = str;
let l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return b64DecodeUnicode(str);
const text = b64DecodeUnicode(str);
if (text !== str) return text;
return orig;
}
function b64encsafe(str) {
@@ -631,6 +637,7 @@ local current_node = map:get(section)
params += opt.query("congestion_control", dom_prefix + "tuic_congestion_control");
params += opt.query("udp_relay_mode", dom_prefix + "tuic_udp_relay_mode");
params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure");
params += opt.query("pcs", dom_prefix + "tls_pinSHA256");
params += "#" + encodeURI(v_alias.value);
if (params[0] == "&") {
@@ -661,6 +668,7 @@ local current_node = map:get(section)
params += opt.query("alpn", dom_prefix + "alpn");
params += opt.query("sni", dom_prefix + "tls_serverName");
params += opt.query("insecure", dom_prefix + "tls_allowInsecure");
params += opt.query("pcs", dom_prefix + "tls_pinSHA256");
}
params += "#" + encodeURI(v_alias.value);
@@ -1001,6 +1009,7 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'method', method || "");
opt.set(dom_prefix + 'ss_method', method || "");
opt.set(dom_prefix + 'tcp_fast_open', queryParam.tfo);
if (plugin && plugin != "none") {
plugin = (plugin === "simple-obfs") ? "obfs-local" : plugin;
opt.set(dom_prefix + 'plugin_enabled', true);
@@ -1023,9 +1032,29 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'tls_allowInsecure', true);
}
}
// Some ShadowTLS subscriptions utilize SIP003.
if (plugin === "shadow-tls") {
pluginOpts = pluginOpts.replace(/(^|;)password=/, '$1passwd=');
pluginOpts = pluginOpts.replace(/(^|;)version=([123])(?=;|$)/, '$1v$2=1');
if (dom_prefix === "ssrust_") {
opt.set(dom_prefix + 'plugin_opts', pluginOpts || "");
} else if (dom_prefix === "singbox_") {
const opts = Object.fromEntries(pluginOpts.split(';').map(item => item.split('=', 2)));
const host = opts.host;
const password = opts.passwd;
const versionKey = Object.keys(opts).find(key => /^v[123]$/.test(key));
const version = versionKey ? versionKey.slice(1) : "1";
opt.set(dom_prefix + 'shadowtls', true);
opt.set(dom_prefix + 'shadowtls_version', version);
opt.set(dom_prefix + 'shadowtls_password', password || "");
opt.set(dom_prefix + 'shadowtls_serverName', host || "");
opt.set(dom_prefix + 'plugin_enabled', false);
}
}
} else {
opt.set(dom_prefix + 'plugin', "none");
}
if (param !== undefined) {
opt.set('remarks', decodeURIComponent(param));
}
@@ -1324,29 +1353,74 @@ local current_node = map:get(section)
}
}
if (link_type === "vmess") {
var sstr = b64DecodeUnicode((link_split[1] || "").replace(/#.*/, "").trim());
if (vmess_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_";
opt.set('type', "sing-box");
} else {
} else if (has_xray) {
dom_prefix = "xray_";
opt.set('type', "Xray");
}
opt.set(dom_prefix + 'protocol', "vmess");
var sstr = b64DecodeUnicode((link_split[1] || "").replace(/#.*/, "").trim());
var ploc = sstr.indexOf("/?");
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
let ssm = {};
if (is_json(sstr)) {
// vmess://base64(json)
ssm = JSON.parse(sstr);
} else if (sstr.indexOf("?") > 0) {
// vmess://base64(auto:uuid@host:port)?tfo=1&remark=xxx&&alterId=0&obfs=websocket&path=%2F&obfsParam=host (obfs ~= ws obfsParam={})
sstr = sstr.replace("/?", "?");
const ploc = sstr.indexOf("?");
let url0 = sstr.substr(0, ploc);
let paramStr = sstr.substr(ploc + 2);
const m = new URL("http://" + b64decsafe(url0));
if (m.username !== "") {
ssm.security = m.username;
ssm.id = m.password || m.username;
} else {
ssm.security = "auto";
ssm.id = m.password || m.username;
}
ssm.add = unbracketIP(m.hostname);
ssm.port = m.port || "443";
const params = {};
const pairs = paramStr.split("&");
for (let i = 0; i < pairs.length; i++) {
const v = pairs[i];
const s = v.indexOf("=");
if (s > 0) {
params[v.substring(0, s)] = decodeURIComponent(v.substring(s + 1));
}
}
ssm.ps = params.remark || params.remarks;
ssm.net = (params.obfs === "websocket") ? "ws" : (params.obfs || "tcp");
ssm.path = params.path;
ssm.aid = params.alterId || "0";
ssm.tls = params.tls;
ssm.sni = params.peer;
ssm.tfo = params.tfo;
if (is_json(params.obfsParam)) {
const op_info = JSON.parse(params.obfsParam);
if (op_info.header) ssm.type = op_info.header;
if (op_info.Host) ssm.host = op_info.Host;
} else {
ssm.host = params.obfsParam;
}
ssm.allowinsecure = params.allowInsecure;
} else {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
return false;
}
var ssm = JSON.parse(sstr);
opt.set('remarks', ssm.ps);
opt.set(dom_prefix + 'address', unbracketIP(ssm.add));
opt.set(dom_prefix + 'port', ssm.port);
opt.set(dom_prefix + 'uuid', ssm.id);
opt.set(dom_prefix + 'security', ssm.security || ssm.scy || "auto");
opt.set(dom_prefix + 'tls', ssm.tls === "tls");
if (ssm.tls === "tls") {
opt.set(dom_prefix + 'tls', ssm.tls === "tls" || ssm.tls === "1");
if (ssm.tls === "tls" || ssm.tls === "1") {
opt.set(dom_prefix + 'tls_serverName', ssm.sni || ssm.host);
opt.set(dom_prefix + 'alpn', ssm.alpn || "default");
if (ssm.fp && ssm.fp !== "") {
@@ -1679,6 +1753,7 @@ local current_node = map:get(section)
dom_prefix + 'tls_allowInsecure',
!((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0' && (queryParam.allow_insecure ?? '0') === '0')
);
opt.set(dom_prefix + 'tls_pinSHA256', queryParam.pcs || queryParam.pinSHA256 || '');
if (hash) {
opt.set('remarks', decodeURIComponent(hash.substr(1)));
}
@@ -1711,29 +1786,23 @@ local current_node = map:get(section)
}
queryParam.security = queryParam.security || "tls";
if (queryParam.security) {
opt.set(dom_prefix + 'tls', true);
if (queryParam.security == "tls") {
opt.set(dom_prefix + 'tls', true);
opt.set(dom_prefix + 'reality', false);
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || queryParam.peer || '');
opt.set(dom_prefix + 'tls_allowInsecure', !((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0'));
if (queryParam.fp && queryParam.fp.trim() != "") {
opt.set(dom_prefix + 'utls', true);
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
}
}
if (queryParam.security == "reality") {
opt.set(dom_prefix + 'tls', true);
opt.set(dom_prefix + 'reality', true);
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || queryParam.peer || '');
if (queryParam.fp && queryParam.fp.trim() != "") {
opt.set(dom_prefix + 'utls', true);
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
}
opt.set(dom_prefix + 'reality_publicKey', queryParam.pbk || '');
opt.set(dom_prefix + 'reality_shortId', queryParam.sid || '');
}
opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || queryParam.peer || '');
if (queryParam.fp && queryParam.fp.trim() != "") {
opt.set(dom_prefix + 'utls', true);
opt.set(dom_prefix + 'fingerprint', queryParam.fp);
}
opt.set(dom_prefix + 'tls_allowInsecure', !((queryParam.allowinsecure ?? '0') === '0' && (queryParam.insecure ?? '0') === '0'));
opt.set(dom_prefix + 'tls_pinSHA256', queryParam.pcs || queryParam.pinSHA256 || '');
}
if (m.hash) {
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
+3
View File
@@ -2345,6 +2345,9 @@ msgstr "صرف‌نظر از گره %s، زیرا گره‌های نوع %s به
msgid "%s type node subscriptions are not currently supported, skip this node."
msgstr "اشتراک‌های گره نوع %s در حال حاضر پشتیبانی نمی‌شوند، صرف‌نظر از این گره."
msgid "Skipping %s node. This node URI format cannot be parsed."
msgstr "از گره %s صرف‌نظر می‌شود. فرمت URI این گره قابل تجزیه نیست."
msgid "Update [%s]"
msgstr "به‌روزرسانی [%s]"
+3
View File
@@ -2346,6 +2346,9 @@ msgstr "Пропуск сервера %s: серверы типа %s требу
msgid "%s type node subscriptions are not currently supported, skip this node."
msgstr "Подписки на серверы типа %s пока не поддерживаются, этот сервер пропускается."
msgid "Skipping %s node. This node URI format cannot be parsed."
msgstr "Пропуск узла %s. Формат URI этого узла не поддается разбору."
msgid "Update [%s]"
msgstr "Обновление [%s]"
+3
View File
@@ -2331,6 +2331,9 @@ msgstr "跳过 %s 节点,因 %s 类型的节点需要 Sing-Box 1.12 以上版
msgid "%s type node subscriptions are not currently supported, skip this node."
msgstr "暂时不支持 %s 类型的节点订阅,跳过此节点。"
msgid "Skipping %s node. This node URI format cannot be parsed."
msgstr "跳过 %s 节点,该节点 URI 格式无法解析。"
msgid "Update [%s]"
msgstr "更新【%s】"
+3
View File
@@ -2337,6 +2337,9 @@ msgstr "跳過 %s 節點,因 %s 類型的節點需要 Sing-Box 1.12 以上版
msgid "%s type node subscriptions are not currently supported, skip this node."
msgstr "暫時不支持 %s 類型的節點訂閱,跳過此節點。"
msgid "Skipping %s node. This node URI format cannot be parsed."
msgstr "跳過 %s 節點,該節點 URI 格式無法解析。"
msgid "Update [%s]"
msgstr "更新【%s】"
@@ -26,7 +26,7 @@ ip6t_m="$ip6t -t mangle -w"
[ -z "$ip6t" -o -z "$(lsmod | grep 'ip6table_mangle')" ] && ip6t_m="eval #$ip6t_m"
FWI=$(uci -q get firewall.passwall2.path 2>/dev/null)
FAKE_IP="198.18.0.0/16"
FAKE_IP_6="fc00::/18"
FAKE_IP_6="2001:2::/48"
factor() {
if [ -z "$1" ] || [ -z "$2" ]; then
@@ -627,21 +627,25 @@ filter_direct_node_list() {
update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && {
ipset -F "$IPSET_WAN"
for wan_ip in $WAN_IP; do
ipset -! add "$IPSET_WAN" "$wan_ip"
done
}
(
flock -x 9 || exit 1
local WAN6_IP=$(get_wan_ips ip6)
[ -n "$WAN6_IP" ] && {
ipset -F "$IPSET_WAN6"
for wan6_ip in $WAN6_IP; do
ipset -! add "$IPSET_WAN6" "$wan6_ip"
done
}
local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && {
# ipset -F "$IPSET_WAN"
for wan_ip in $WAN_IP; do
ipset -! add "$IPSET_WAN" "$wan_ip"
done
}
local WAN6_IP=$(get_wan_ips ip6)
[ -n "$WAN6_IP" ] && {
# ipset -F "$IPSET_WAN6"
for wan6_ip in $WAN6_IP; do
ipset -! add "$IPSET_WAN6" "$wan6_ip"
done
}
) 9>"${LOCK_PATH}/${CONFIG}_update_wan_sets.lock"
}
add_firewall_rule() {
@@ -18,7 +18,7 @@ FWMARK="0x50535732"
FWI=$(uci -q get firewall.passwall2.path 2>/dev/null)
FAKE_IP="198.18.0.0/16"
FAKE_IP_6="fc00::/18"
FAKE_IP_6="2001:2::/48"
factor() {
local ports="$1"
@@ -686,17 +686,21 @@ mwan3_start() {
update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && {
nft flush set $NFTABLE_NAME $NFTSET_WAN
echo "$WAN_IP" | insert_nftset $NFTSET_WAN "-1"
}
(
flock -x 9 || exit 1
local WAN6_IP=$(get_wan_ips ip6)
[ -n "${WAN6_IP}" ] && {
nft flush set $NFTABLE_NAME $NFTSET_WAN6
echo "$WAN6_IP" | insert_nftset $NFTSET_WAN6 "-1"
}
local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && {
# nft flush set $NFTABLE_NAME $NFTSET_WAN
echo "$WAN_IP" | insert_nftset $NFTSET_WAN
}
local WAN6_IP=$(get_wan_ips ip6)
[ -n "${WAN6_IP}" ] && {
# nft flush set $NFTABLE_NAME $NFTSET_WAN6
echo "$WAN6_IP" | insert_nftset $NFTSET_WAN6
}
) 9>"${LOCK_PATH}/${CONFIG}_update_wan_sets.lock"
}
add_firewall_rule() {
@@ -900,7 +900,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
-- if ssr_group then result.ssr_group = ssr_group end
result.remarks = base64Decode(params.remarks)
elseif szType == 'vmess' then
local info = jsonParse(content)
if sub_vmess_type == "sing-box" and has_singbox then
result.type = 'sing-box'
elseif sub_vmess_type == "xray" and has_xray then
@@ -909,6 +908,58 @@ local function processData(szType, content, add_mode, group, sub_cfg)
log(2, i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "VMess", "VMess"))
return nil
end
-- vmess://base64(json)
local info = jsonParse(content)
if not info then
-- vmess://base64(auto:uuid@host:port)?tfo=1&remark=xxx&&alterId=0&obfs=websocket&path=%2F&obfsParam=host (obfs ~= ws obfsParam={})
if content:find("?", 1, true) then
info = {}
local Info = split(content:gsub("/%?", "?"), "%?")
local sp = split(base64Decode(Info[1]), "@")
local id_info = split(sp[1], ":")
info.security = (#id_info > 1 and id_info[1] ~= "") and id_info[1] or "auto"
info.id = id_info[#id_info]
local addr, port = sp[2], "443"
if api.is_ipv6addrport(addr) then
local a, p = addr:match("^%[(.+)%]:(%d+)$")
if a then addr, port = a, p end
addr = api.get_ipv6_only(addr)
else
local host_port = split(addr, ":")
addr = host_port[1]
if #host_port > 1 then port = host_port[#host_port] end
end
info.add, info.port = addr, port
local params = {}
for _, v in pairs(split(Info[2], '&')) do
local s = v:find("=", 1, true)
if s and s > 1 then
params[v:sub(1, s - 1)] = UrlDecode(v:sub(s + 1))
end
end
info.ps = params.remark or params.remarks
info.net = (params.obfs == "websocket") and "ws" or (params.obfs or "tcp")
info.path = params.path
info.aid = params.alterId or "0"
info.tls = params.tls
info.sni = params.peer
info.tfo = params.tfo
local op_info = jsonParse(params.obfsParam)
if op_info then
if op_info.header then info.type = op_info.header end
if op_info.Host then info.host = op_info.Host end
else
info.host = params.obfsParam
end
info.allowinsecure = params.allowInsecure
else
log(2, i18n.translatef("Skipping %s node. This node URI format cannot be parsed."))
return nil
end
end
result.address = info.add
result.port = info.port
result.protocol = 'vmess'
@@ -1068,6 +1119,11 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if idx_pn then
result.plugin = plugin_info:sub(1, idx_pn - 1)
result.plugin_opts = plugin_info:sub(idx_pn + 1, #plugin_info)
-- Some ShadowTLS subscriptions utilize SIP003.
result.plugin_opts = result.plugin_opts:gsub("^password=", "passwd=")
result.plugin_opts = result.plugin_opts:gsub(";password=", ";passwd=")
result.plugin_opts = result.plugin_opts:gsub("^version=([123])", "v%1=1")
result.plugin_opts = result.plugin_opts:gsub(";version=([123])", ";v%1=1")
else
result.plugin = plugin_info
end
@@ -1174,10 +1230,22 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.plugin_opts = nil
end
elseif result.type == 'sing-box' then
if result.plugin ~= "obfs-local" and result.plugin ~= "v2ray-plugin" then
if result.plugin ~= "obfs-local" and result.plugin ~= "v2ray-plugin" and result.plugin ~= "shadow-tls" then
result.error_msg = i18n.translatef("%s unsupport SS %s plugin.", "Sing-Box", result.plugin)
else
result.plugin_enabled = "1"
-- Some ShadowTLS subscriptions utilize SIP003.
if result.plugin == "shadow-tls" then
for item in result.plugin_opts:gmatch("[^;]+") do
local key, value = item:match("^([^=]+)=(.*)$")
if key == "host" then result.shadowtls_serverName = value end
if key == "passwd" then result.shadowtls_password = value end
if key:match("^v[123]$") then result.shadowtls_version = key:sub(2) end
end
result.shadowtls = "1"
result.plugin_opts = nil
result.plugin_enabled = nil
end
end
else
result.plugin_enabled = "1"
@@ -1722,6 +1790,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.hysteria_auth_type = "string"
result.hysteria_auth_password = params.auth
result.tls_serverName = params.peer or params.sni or ""
result.tls_pinSHA256 = params.pcs or params.pinSHA256
local insecure = params.allowinsecure or params.allowInsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.alpn = params.alpn
@@ -1845,6 +1914,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end
result.tls_serverName = params.sni
result.tls_disable_sni = params.disable_sni
result.tls_pinSHA256 = params.pcs or params.pinsha256
result.tuic_alpn = params.alpn or "h3"
result.tuic_congestion_control = params.congestion_control or "cubic"
result.tuic_udp_relay_mode = params.udp_relay_mode or "native"
@@ -1900,6 +1970,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if params.security == "tls" or params.security == "reality" then
result.tls = "1"
result.tls_serverName = params.sni or params.peer
result.tls_pinSHA256 = params.pcs or params.pinsha256
result.alpn = params.alpn
if params.fp and params.fp ~= "" then
result.utls = "1"
@@ -2063,6 +2134,9 @@ local function curl(url, file, ua, mode, hwid)
if hwid == "1" then
curl_args[#curl_args + 1] = get_headers()
end
local cookie_file = "/tmp/cookie_" .. api.gen_random_char(5)
curl_args[#curl_args + 1] = '-c "' .. cookie_file .. '" -b "' .. cookie_file .. '"'
local return_code, result
if mode == "direct" then
return_code, result = api.curl_direct(url, file, curl_args)
@@ -2071,6 +2145,8 @@ local function curl(url, file, ua, mode, hwid)
else
return_code, result = api.curl_auto(url, file, curl_args)
end
luci.sys.call('rm -f "%s"' % cookie_file)
return return_code, tonumber(result)
end
@@ -2599,7 +2675,7 @@ local execute = function()
f:close()
local raw_data = api.trim(stdout)
local old_md5 = value.md5 or ""
local new_md5 = luci.sys.exec("md5sum " .. tmp_file .. " 2>/dev/null | awk '{print $1}'"):gsub("\n", "")
local new_md5 = luci.sys.exec("md5sum " .. tmp_file .. " 2>/dev/null | awk '{printf \"%s\", $1}'")
if not manual_sub and old_md5 == new_md5 then
log(1, i18n.translatef("Subscription: [%s] No changes, no update required.", remark))
else