소스 검색

luci-app-ssr-plus: Fix the trojan of Xray and optimize the shadowsocks subscribe of Xray.

zxl hhyccc 1 주 전
부모
커밋
ebb246820e
1개의 변경된 파일354개의 추가작업 그리고 381개의 파일을 삭제
  1. 354 381
      luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua

+ 354 - 381
luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua

@@ -173,34 +173,6 @@ local function isCompleteJSON(str)
 	local success, _ = pcall(jsonParse, str)
 	return success
 end
-local function detectNodeType(rawContent)
-    -- 去掉 # 前后空格,处理 HTML 转义 &
-    local content = trim(rawContent:gsub("&[a-zA-Z]+;", "&"):gsub("%s*#%s*", "#"))
-
-    -- 找到 # 分隔位置
-    local idx_sp = content:find("#") or 0
-    local info = content:sub(1, idx_sp > 0 and idx_sp - 1 or #content):gsub("/%?", "?")
-
-    -- 拆 base64 主体和 ? 参数部分
-    local uri_main, query_str = info:match("^([^?]+)%??(.*)$")
-
-    local params = {}
-    if query_str and query_str ~= "" then
-        for _, v in ipairs(split(query_str, '&')) do
-            local t = split(v, '=')
-            if #t >= 2 then
-                params[t[1]] = UrlDecode(t[2])
-            end
-        end
-    end
-
-    -- 判断是否是 Xray-SS 节点
-    if params["type"] then
-        return "shadowsocks"
-    else
-        return "ss"
-    end
-end
 -- 处理数据
 local function processData(szType, content)
 	local result = {type = szType, local_port = 1234, kcp_param = '--nocomp'}
@@ -211,14 +183,6 @@ local function processData(szType, content)
 		end
 	end
 
-	-- 协议头识别
-	if szType == "ss" then
-		local nodeType = detectNodeType(content)
-		if nodeType == "shadowsocks" then
-			szType = "shadowsocks"  -- 替换类型
-		end
-	end
-
 	if szType == "hysteria2" or szType == "hy2" then
 		local url = URL.parse("http://" .. content)
 		local params = url.query
@@ -261,7 +225,7 @@ local function processData(szType, content)
 			end
 			if params.alpn then
 				local alpn = {}
-				for v in params.alpn:gmatch("[^,]+") do
+				for v in params.alpn:gmatch("[^,;|%s]+") do
 					table.insert(alpn, v)
 				end
 				result.tls_alpn = alpn
@@ -435,7 +399,6 @@ local function processData(szType, content)
 			result.security = info.security
 		end
 	elseif szType == "ss" then
-		local content = trim(content:gsub("&[a-zA-Z]+;", "&"):gsub("%s*#%s*", "#"))
 		local idx_sp = content:find("#") or 0
 		local alias = ""
 		if idx_sp > 0 then
@@ -456,153 +419,255 @@ local function processData(szType, content)
 			end
 		end
 
-		local is_old_format = uri_main:find("@") and not uri_main:find("://.*@")
-		local base64_str, host_port, userinfo, server, port, method, password
+		if not params.type or params.type == "" then
+			local is_old_format = uri_main:find("@") and not uri_main:find("://.*@")
+			local base64_str, host_port, userinfo, server, port, method, password
 
-		if is_old_format then
-			-- 旧格式:base64(method:pass)@host:port
-			base64_str, host_port = uri_main:match("^([^@]+)@(.-)$")
-			log("SS 节点旧格式解析:", base64_str)
-			if not base64_str or not host_port then
-				log("SS 节点旧格式解析失败:", uri_main)
-				return nil
-			end
-			local decoded = base64Decode(UrlDecode(base64_str))
-			if not decoded then
-				log("SS base64 解码失败(旧格式):", base64_str)
-				return nil
-			end
-			userinfo = decoded
-		else
-			-- 新格式:base64(method:pass@host:port)
-			local decoded = base64Decode(UrlDecode(uri_main))
-			if not decoded then
-				log("SS base64 解码失败(新格式):", uri_main)
-				return nil
+			if is_old_format then
+				-- 旧格式:base64(method:pass)@host:port
+				base64_str, host_port = uri_main:match("^([^@]+)@(.-)$")
+				log("SS 节点旧格式解析:", base64_str)
+				if not base64_str or not host_port then
+					log("SS 节点旧格式解析失败:", uri_main)
+					return nil
+				end
+				local decoded = base64Decode(UrlDecode(base64_str))
+				if not decoded then
+					log("SS base64 解码失败(旧格式):", base64_str)
+					return nil
+				end
+				userinfo = decoded
+			else
+				-- 新格式:base64(method:pass@host:port)
+				local decoded = base64Decode(UrlDecode(uri_main))
+				if not decoded then
+					log("SS base64 解码失败(新格式):", uri_main)
+					return nil
+				end
+				userinfo, host_port = decoded:match("^(.-)@(.-)$")
+				if not userinfo or not host_port then
+					log("SS 解码内容缺失 @ 分隔:", decoded)
+					return nil
+				end
 			end
-			userinfo, host_port = decoded:match("^(.-)@(.-)$")
-			if not userinfo or not host_port then
-				log("SS 解码内容缺失 @ 分隔:", decoded)
+
+			-- 解析加密方式和密码(允许密码包含冒号)
+			local split_pos = userinfo:find(":")
+			if not split_pos then
+				log("SS 用户信息格式错误:", userinfo)
 				return nil
 			end
-		end
-
-		-- 解析加密方式和密码(允许密码包含冒号)
-		local split_pos = userinfo:find(":")
-		if not split_pos then
-			log("SS 用户信息格式错误:", userinfo)
-			return nil
-		end
-		method = userinfo:sub(1, split_pos - 1)
-		password = userinfo:sub(split_pos + 1)
+			method = userinfo:sub(1, split_pos - 1)
+			password = userinfo:sub(split_pos + 1)
 
-		-- 判断密码是否经过url编码
-		local function isURLEncodedPassword(pwd)
-			if not pwd:find("%%[0-9A-Fa-f][0-9A-Fa-f]") then
-				return false
+			-- 判断密码是否经过url编码
+			local function isURLEncodedPassword(pwd)
+				if not pwd:find("%%[0-9A-Fa-f][0-9A-Fa-f]") then
+					return false
+				end
+					local ok, decoded = pcall(UrlDecode, pwd)
+					return ok and urlEncode(decoded) == pwd
 			end
-				local ok, decoded = pcall(UrlDecode, pwd)
-				return ok and urlEncode(decoded) == pwd
-		end
 
-		local decoded = UrlDecode(password)
-			if isURLEncodedPassword(password) and decoded then
-				password = decoded
-		end
-
-		-- 解析服务器地址和端口(兼容 IPv6)
-		if host_port:find("^%[.*%]:%d+$") then
-			server, port = host_port:match("^%[(.*)%]:(%d+)$")
-		else
-			server, port = host_port:match("^(.-):(%d+)$")
-		end
-		if not server or not port then
-			log("SS 节点服务器信息格式错误:", host_port)
-			return nil
-		end
-
-		-- 如果 SS 程序未安装则跳过订阅	
-		if not (v2_ss or has_ss_type) then
-			return nil
-		end
-
-		-- 填充 result
-		result.alias = alias
-		result.type = v2_ss
-		result.v2ray_protocol = (v2_ss == "v2ray") and "shadowsocks" or nil
-		result.has_ss_type = has_ss_type
-		result.encrypt_method_ss = method
-		result.password = password
-		result.server = server
-		result.server_port = port
-
-		-- 仅在 v2ray + shadowsocks 协议时处理 ECH
-		if v2_ss == "v2ray" and result.v2ray_protocol == "shadowsocks" then
-			if params.ech and params.ech ~= "" then
-				result.enable_ech = "1"
-				result.ech_config = ech
+			local decoded = UrlDecode(password)
+				if isURLEncodedPassword(password) and decoded then
+					password = decoded
 			end
-		end
 
-		-- 插件处理
-		if params.plugin then
-			local plugin_info = UrlDecode(params.plugin)
-			local idx_pn = plugin_info:find(";")
-			if idx_pn then
-				result.plugin = plugin_info:sub(1, idx_pn - 1)
-				result.plugin_opts = plugin_info:sub(idx_pn + 1, #plugin_info)
+			-- 解析服务器地址和端口(兼容 IPv6)
+			if host_port:find("^%[.*%]:%d+$") then
+				server, port = host_port:match("^%[(.*)%]:(%d+)$")
 			else
-				result.plugin = plugin_info
-				result.plugin_opts = ""
+				server, port = host_port:match("^(.-):(%d+)$")
 			end
-			-- 部分机场下发的插件名为 simple-obfs,这里应该改为 obfs-local
-			if result.plugin == "simple-obfs" then
-				result.plugin = "obfs-local"
+			if not server or not port then
+				log("SS 节点服务器信息格式错误:", host_port)
+				return nil
 			end
-			-- 如果插件不为 none,确保 enable_plugin 为 1
-			if result.plugin ~= "none" and result.plugin ~= "" then
-				result.enable_plugin = 1
+
+			-- 如果 SS 程序未安装则跳过订阅	
+			if not (v2_ss or has_ss_type) then
+				return nil
 			end
-		elseif has_ss_type and has_ss_type ~= "ss-libev" then
-			if params["shadow-tls"] then
-				-- 特别处理 shadow-tls 作为插件
-				-- log("原始 shadow-tls 参数:", params["shadow-tls"])
-				local decoded_tls = base64Decode(UrlDecode(params["shadow-tls"]))
-				--log("SS 节点 shadow-tls 解码后:", decoded_tls or "nil")
-				if decoded_tls then
-					local ok, st = pcall(jsonParse, decoded_tls)
-					if ok and st then
-
-						result.plugin = "shadow-tls"
-						result.enable_plugin = 1
+
+			-- 填充 result
+			result.alias = alias
+			result.type = v2_ss
+			result.v2ray_protocol = (v2_ss == "v2ray") and "shadowsocks" or nil
+			result.has_ss_type = has_ss_type
+			result.encrypt_method_ss = method
+			result.password = password
+			result.server = server
+			result.server_port = port
+
+			-- 仅在 v2ray + shadowsocks 协议时处理 ECH
+			if v2_ss == "v2ray" and result.v2ray_protocol == "shadowsocks" then
+				if params.ech and params.ech ~= "" then
+					result.enable_ech = "1"
+					result.ech_config = ech
+				end
+			end
+
+			-- 插件处理
+			if params.plugin then
+				local plugin_info = UrlDecode(params.plugin)
+				local idx_pn = plugin_info:find(";")
+				if idx_pn then
+					result.plugin = plugin_info:sub(1, idx_pn - 1)
+					result.plugin_opts = plugin_info:sub(idx_pn + 1, #plugin_info)
+				else
+					result.plugin = plugin_info
+					result.plugin_opts = ""
+				end
+				-- 部分机场下发的插件名为 simple-obfs,这里应该改为 obfs-local
+				if result.plugin == "simple-obfs" then
+					result.plugin = "obfs-local"
+				end
+				-- 如果插件不为 none,确保 enable_plugin 为 1
+				if result.plugin ~= "none" and result.plugin ~= "" then
+					result.enable_plugin = 1
+				end
+			elseif has_ss_type and has_ss_type ~= "ss-libev" then
+				if params["shadow-tls"] then
+					-- 特别处理 shadow-tls 作为插件
+					-- log("原始 shadow-tls 参数:", params["shadow-tls"])
+					local decoded_tls = base64Decode(UrlDecode(params["shadow-tls"]))
+					--log("SS 节点 shadow-tls 解码后:", decoded_tls or "nil")
+					if decoded_tls then
+						local ok, st = pcall(jsonParse, decoded_tls)
+						if ok and st then
+
+							result.plugin = "shadow-tls"
+							result.enable_plugin = 1
 					
-						local version_flag = ""
-						if st.version and tonumber(st.version) then
-					    	version_flag = string.format("v%s=1;", st.version)
-						end
+							local version_flag = ""
+							if st.version and tonumber(st.version) then
+					    		version_flag = string.format("v%s=1;", st.version)
+							end
 					
-						-- 合成 plugin_opts 格式:v%s=1;host=xxx;password=xxx
-						result.plugin_opts = string.format("%shost=%s;passwd=%s",
-					    	version_flag,
-							st.host or "",
-							st.password or "")
-					else
-						log("shadow-tls JSON 解析失败")
+							-- 合成 plugin_opts 格式:v%s=1;host=xxx;password=xxx
+							result.plugin_opts = string.format("%shost=%s;passwd=%s",
+					    		version_flag,
+								st.host or "",
+								st.password or "")
+						else
+							log("shadow-tls JSON 解析失败")
+						end
 					end
 				end
+			else
+				if params["shadow-tls"] then
+					log("错误:ShadowSocks-libev 不支持使用 shadow-tls 插件")
+					return nil, "ShadowSocks-libev 不支持使用 shadow-tls 插件"
+				end
+			end
+
+			-- 检查加密方法是否受支持
+			if not checkTabValue(encrypt_methods_ss)[method] then
+				-- 1202 年了还不支持 SS AEAD 的屑机场
+				-- log("不支持的SS加密方法:", method)
+				result.server = nil
 			end
 		else
-			if params["shadow-tls"] then
-				log("错误:ShadowSocks-libev 不支持使用 shadow-tls 插件")
-				return nil, "ShadowSocks-libev 不支持使用 shadow-tls 插件"
+			local url = URL.parse("http://" .. info)
+			local params = url.query
+
+			result.alias = alias
+			result.type = "v2ray"
+			result.v2ray_protocol = "shadowsocks"
+			result.server = url.host
+			result.server_port = url.port
+
+			-- 判断 @ 前部分是否为 Base64
+			local is_base64_decoded = base64Decode(UrlDecode(url.user))
+			if is_base64_decoded:find(":") then
+        		-- 新格式:method:password
+        		result.encrypt_method_ss, result.password = is_base64_decoded:match("^(.-):(.*)$")
+			else
+        		-- 旧格式:UUID 直接作为密码
+        		result.password = url.user
+        		result.encrypt_method_ss = params.encryption or "none"
 			end
-		end
 
-		-- 检查加密方法是否受支持
-		if not checkTabValue(encrypt_methods_ss)[method] then
-			-- 1202 年了还不支持 SS AEAD 的屑机场
-			-- log("不支持的SS加密方法:", method)
-			result.server = nil
+			result.transport = params.type or "raw"
+			if result.transport == "tcp" then
+				result.transport = "raw"
+			end
+			if result.transport == "splithttp" then
+				result.transport = "xhttp"
+			end
+			result.tls = (params.security == "tls" or params.security == "xtls") and "1" or "0"
+			if params.alpn and params.alpn ~= "" then
+				local alpn = {}
+				for v in params.alpn:gmatch("[^,;|%s]+") do
+					table.insert(alpn, v)
+				end
+				result.tls_alpn = params.alpn
+			end
+			result.tls_host = params.sni
+			result.tls_flow = (params.security == "tls" or params.security == "reality") and params.flow or nil
+			result.fingerprint = params.fp
+			result.reality = (params.security == "reality") and "1" or "0"
+			result.reality_publickey = params.pbk and UrlDecode(params.pbk) or nil
+			result.reality_shortid = params.sid
+			result.reality_spiderx = params.spx and UrlDecode(params.spx) or nil
+			-- 检查 ech 参数是否存在且非空
+			result.enable_ech = (params.ech and params.ech ~= "") and "1" or nil
+			result.ech_config = (params.ech and params.ech ~= "") and params.ech or nil
+			-- 检查 pqv 参数是否存在且非空
+			result.enable_mldsa65verify = (params.pqv and params.pqv ~= "") and "1" or nil
+			result.reality_mldsa65verify = (params.pqv and params.pqv ~= "") and params.pqv or nil
+			if result.transport == "ws" then
+				result.ws_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
+				result.ws_path = params.path and UrlDecode(params.path) or "/"
+			elseif result.transport == "httpupgrade" then
+				result.httpupgrade_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
+				result.httpupgrade_path = params.path and UrlDecode(params.path) or "/"
+			elseif result.transport == "xhttp" or result.transport == "splithttp" then
+				result.xhttp_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
+				result.xhttp_mode = params.mode or "auto"
+				result.xhttp_path = params.path and UrlDecode(params.path) or "/"
+				-- 检查 extra 参数是否存在且非空
+				result.enable_xhttp_extra = (params.extra and params.extra ~= "") and "1" or nil
+				result.xhttp_extra = (params.extra and params.extra ~= "") and params.extra or nil
+				-- 尝试解析 JSON 数据
+				local success, Data = pcall(jsonParse, params.extra or "")
+				if success and type(Data) == "table" then
+					local address = (Data.extra and Data.extra.downloadSettings and Data.extra.downloadSettings.address)
+						or (Data.downloadSettings and Data.downloadSettings.address)
+					result.download_address = address and address ~= "" and address or nil
+				else
+					-- 如果解析失败,清空下载地址
+					result.download_address = nil
+				end
+			-- make it compatible with bullshit, "h2" transport is non-existent at all
+			elseif result.transport == "http" or result.transport == "h2" then
+				result.transport = "h2"
+				result.h2_host = params.host and UrlDecode(params.host) or nil
+				result.h2_path = params.path and UrlDecode(params.path) or nil
+			elseif result.transport == "kcp" then
+				result.kcp_guise = params.headerType or "none"
+				result.seed = params.seed
+				result.mtu = 1350
+				result.tti = 50
+				result.uplink_capacity = 5
+				result.downlink_capacity = 20
+				result.read_buffer_size = 2
+				result.write_buffer_size = 2
+			elseif result.transport == "quic" then
+				result.quic_guise = params.headerType or "none"
+				result.quic_security = params.quicSecurity or "none"
+				result.quic_key = params.key
+			elseif result.transport == "grpc" then
+				result.serviceName = params.serviceName
+				result.grpc_mode = params.mode or "gun"
+			elseif result.transport == "tcp" or result.transport == "raw" then
+				result.tcp_guise = params.headerType or "none"
+			if result.tcp_guise == "http" then
+					result.tcp_host = params.host and UrlDecode(params.host) or nil
+					result.tcp_path = params.path and UrlDecode(params.path) or nil
+				end
+			end
 		end
 	elseif szType == "sip008" then
 		result.type = v2_ss
@@ -661,18 +726,16 @@ local function processData(szType, content)
 		-- 分离服务器地址和端口
 		local hostParts = split(hostPort, ":")
 
-		local server = hostParts[1]
-		local port = hostParts[2]
-
 		-- 验证服务器地址和端口
 		if #hostParts < 2 then
 			--log("Trojan节点格式错误: 缺少端口号")
 			return nil
 		end
 
+		local server = hostParts[1]
+		local port = hostParts[2]
+
 		result.alias = UrlDecode(alias)
-		result.type = v2_tj
-		result.v2ray_protocol = "trojan"
 		result.server = server
 		result.password = userinfo
 
@@ -687,8 +750,8 @@ local function processData(szType, content)
 			result.server_port = queryParts[1]
 
 			-- 解析查询参数
-			for _, v in pairs(split(queryParts[2], '&')) do
-				local t = split(v, '=')
+			for _, v in pairs(split(queryParts[2], "&")) do
+				local t = split(v, "=")
 				if #t >= 2 then
 					params[t[1]] = t[2]
 				end
@@ -709,82 +772,93 @@ local function processData(szType, content)
 				-- 处理 insecure 参数
 				result.insecure = params.allowInsecure
 			end
+			if params.tfo then
+				-- 处理 insecure 参数
+				result.fast_open = params.tfo
+			end
 		else
 			result.server_port = port
 		end
 
 		-- 如果 Tojan 程序未安装则跳过订阅	
-		if not v2_tj then
+		if not v2_tj or v2_tj == "" then
 			return nil
 		end
 
-		if v2_tj ~= "trojan" then
-			if params.fp then
-				-- 处理 fingerprint 参数
-				result.fingerprint = params.fp
-			end
-			-- 处理 ech 参数
-			if params.ech then
-				result.enable_ech = "1"
-				result.ech_config = params.ech
-			end
-			-- 处理传输协议
-			result.transport = params.type or "raw" -- 默认传输协议为 raw
-			if result.transport == "tcp" then
-				result.transport = "raw"
-			end
-			if result.transport == "splithttp" then
-				result.transport = "xhttp"
-			end
-			if result.transport == "ws" then
-				result.ws_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
-				result.ws_path = params.path and UrlDecode(params.path) or "/"
-			elseif result.transport == "httpupgrade" then
-				result.httpupgrade_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
-				result.httpupgrade_path = params.path and UrlDecode(params.path) or "/"
-			elseif result.transport == "xhttp" or result.transport == "splithttp" then
-				result.xhttp_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
-				result.xhttp_mode = params.mode or "auto"
-				result.xhttp_path = params.path and UrlDecode(params.path) or "/"
-				-- 检查 extra 参数是否存在且非空
-				result.enable_xhttp_extra = (params.extra and params.extra ~= "") and "1" or nil
-				result.xhttp_extra = (params.extra and params.extra ~= "") and params.extra or nil
-				-- 尝试解析 JSON 数据
-				local success, Data = pcall(jsonParse, params.extra or "")
-				if success and type(Data) == "table" then
-					local address = (Data.extra and Data.extra.downloadSettings and Data.extra.downloadSettings.address)
-						or (Data.downloadSettings and Data.downloadSettings.address)
-					result.download_address = address and address ~= "" and address or nil
-				else
-					-- 如果解析失败,清空下载地址
-					result.download_address = nil
+		if params.type and params.type ~= "" then
+			v2_tj = "v2ray"
+			result.type = v2_tj
+			result.v2ray_protocol = "trojan"
+			if v2_tj ~= "trojan" then
+				if params.fp then
+					-- 处理 fingerprint 参数
+					result.fingerprint = params.fp
 				end
-			elseif result.transport == "http" or result.transport == "h2" then
-				result.transport = "h2"
-				result.h2_host = params.host and UrlDecode(params.host) or nil
-				result.h2_path = params.path and UrlDecode(params.path) or nil
-			elseif result.transport == "kcp" then
-				result.kcp_guise = params.headerType or "none"
-				result.seed = params.seed
-				result.mtu = 1350
-				result.tti = 50
-				result.uplink_capacity = 5
-				result.downlink_capacity = 20
-				result.read_buffer_size = 2
-				result.write_buffer_size = 2
-			elseif result.transport == "quic" then
-				result.quic_guise = params.headerType or "none"
-				result.quic_security = params.quicSecurity or "none"
-				result.quic_key = params.key
-			elseif result.transport == "grpc" then
-				result.serviceName = params.serviceName
-				result.grpc_mode = params.mode or "gun"
-			elseif result.transport == "tcp" or result.transport == "raw" then
-				result.tcp_guise = params.headerType and params.headerType ~= "" and params.headerType or "none"
-				if result.tcp_guise == "http" then
-					result.tcp_host = params.host and UrlDecode(params.host) or nil
-					result.tcp_path = params.path and UrlDecode(params.path) or nil
+				-- 处理 ech 参数
+				if params.ech then
+					result.enable_ech = "1"
+					result.ech_config = params.ech
 				end
+				-- 处理传输协议
+				result.transport = params.type or "raw" -- 默认传输协议为 raw
+				if result.transport == "tcp" then
+					result.transport = "raw"
+				end
+				if result.transport == "splithttp" then
+					result.transport = "xhttp"
+				end
+				if result.transport == "ws" then
+					result.ws_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
+					result.ws_path = params.path and UrlDecode(params.path) or "/"
+				elseif result.transport == "httpupgrade" then
+					result.httpupgrade_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
+					result.httpupgrade_path = params.path and UrlDecode(params.path) or "/"
+				elseif result.transport == "xhttp" or result.transport == "splithttp" then
+					result.xhttp_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
+					result.xhttp_mode = params.mode or "auto"
+					result.xhttp_path = params.path and UrlDecode(params.path) or "/"
+					-- 检查 extra 参数是否存在且非空
+					result.enable_xhttp_extra = (params.extra and params.extra ~= "") and "1" or nil
+					result.xhttp_extra = (params.extra and params.extra ~= "") and params.extra or nil
+					-- 尝试解析 JSON 数据
+					local success, Data = pcall(jsonParse, params.extra or "")
+					if success and type(Data) == "table" then
+						local address = (Data.extra and Data.extra.downloadSettings and Data.extra.downloadSettings.address)
+							or (Data.downloadSettings and Data.downloadSettings.address)
+						result.download_address = address and address ~= "" and address or nil
+					else
+						-- 如果解析失败,清空下载地址
+						result.download_address = nil
+					end
+				elseif result.transport == "http" or result.transport == "h2" then
+					result.transport = "h2"
+					result.h2_host = params.host and UrlDecode(params.host) or nil
+					result.h2_path = params.path and UrlDecode(params.path) or nil
+				elseif result.transport == "kcp" then
+					result.kcp_guise = params.headerType or "none"
+					result.seed = params.seed
+					result.mtu = 1350
+					result.tti = 50
+					result.uplink_capacity = 5
+					result.downlink_capacity = 20
+					result.read_buffer_size = 2
+					result.write_buffer_size = 2
+				elseif result.transport == "quic" then
+					result.quic_guise = params.headerType or "none"
+					result.quic_security = params.quicSecurity or "none"
+					result.quic_key = params.key
+				elseif result.transport == "grpc" then
+					result.serviceName = params.serviceName
+					result.grpc_mode = params.mode or "gun"
+				elseif result.transport == "tcp" or result.transport == "raw" then
+					result.tcp_guise = params.headerType and params.headerType ~= "" and params.headerType or "none"
+					if result.tcp_guise == "http" then
+						result.tcp_host = params.host and UrlDecode(params.host) or nil
+						result.tcp_path = params.path and UrlDecode(params.path) or nil
+					end
+				end
+			else
+				result.type = v2_tj
 			end
 		end
 	elseif szType == "vless" then
@@ -808,114 +882,7 @@ local function processData(szType, content)
 		result.tls = (params.security == "tls" or params.security == "xtls") and "1" or "0"
 		if params.alpn and params.alpn ~= "" then
 			local alpn = {}
-			for v in params.alpn:gmatch("[^,]+") do
-				table.insert(alpn, v)
-			end
-			result.tls_alpn = alpn
-		end
-		result.tls_host = params.sni
-		result.tls_flow = (params.security == "tls" or params.security == "reality") and params.flow or nil
-		result.fingerprint = params.fp
-		result.reality = (params.security == "reality") and "1" or "0"
-		result.reality_publickey = params.pbk and UrlDecode(params.pbk) or nil
-		result.reality_shortid = params.sid
-		result.reality_spiderx = params.spx and UrlDecode(params.spx) or nil
-		-- 检查 ech 参数是否存在且非空
-		result.enable_ech = (params.ech and params.ech ~= "") and "1" or nil
-		result.ech_config = (params.ech and params.ech ~= "") and params.ech or nil
-		-- 检查 pqv 参数是否存在且非空
-		result.enable_mldsa65verify = (params.pqv and params.pqv ~= "") and "1" or nil
-		result.reality_mldsa65verify = (params.pqv and params.pqv ~= "") and params.pqv or nil
-		if result.transport == "ws" then
-			result.ws_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
-			result.ws_path = params.path and UrlDecode(params.path) or "/"
-		elseif result.transport == "httpupgrade" then
-			result.httpupgrade_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
-			result.httpupgrade_path = params.path and UrlDecode(params.path) or "/"
-		elseif result.transport == "xhttp" or result.transport == "splithttp" then
-			result.xhttp_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil
-			result.xhttp_mode = params.mode or "auto"
-			result.xhttp_path = params.path and UrlDecode(params.path) or "/"
-			-- 检查 extra 参数是否存在且非空
-			result.enable_xhttp_extra = (params.extra and params.extra ~= "") and "1" or nil
-			result.xhttp_extra = (params.extra and params.extra ~= "") and params.extra or nil
-			-- 尝试解析 JSON 数据
-			local success, Data = pcall(jsonParse, params.extra or "")
-			if success and type(Data) == "table" then
-				local address = (Data.extra and Data.extra.downloadSettings and Data.extra.downloadSettings.address)
-					or (Data.downloadSettings and Data.downloadSettings.address)
-				result.download_address = address and address ~= "" and address or nil
-			else
-				-- 如果解析失败,清空下载地址
-				result.download_address = nil
-			end
-		-- make it compatible with bullshit, "h2" transport is non-existent at all
-		elseif result.transport == "http" or result.transport == "h2" then
-			result.transport = "h2"
-			result.h2_host = params.host and UrlDecode(params.host) or nil
-			result.h2_path = params.path and UrlDecode(params.path) or nil
-		elseif result.transport == "kcp" then
-			result.kcp_guise = params.headerType or "none"
-			result.seed = params.seed
-			result.mtu = 1350
-			result.tti = 50
-			result.uplink_capacity = 5
-			result.downlink_capacity = 20
-			result.read_buffer_size = 2
-			result.write_buffer_size = 2
-		elseif result.transport == "quic" then
-			result.quic_guise = params.headerType or "none"
-			result.quic_security = params.quicSecurity or "none"
-			result.quic_key = params.key
-		elseif result.transport == "grpc" then
-			result.serviceName = params.serviceName
-			result.grpc_mode = params.mode or "gun"
-		elseif result.transport == "tcp" or result.transport == "raw" then
-			result.tcp_guise = params.headerType or "none"
-			if result.tcp_guise == "http" then
-				result.tcp_host = params.host and UrlDecode(params.host) or nil
-				result.tcp_path = params.path and UrlDecode(params.path) or nil
-			end
-		end
-	elseif szType == "shadowsocks" then
-		local content = trim(content:gsub("&[a-zA-Z]+;", "&"):gsub("%s*#%s*", "#"))
-		local idx_sp = content:find("#") or 0
-		local alias = ""
-		if idx_sp > 0 then
-        	alias = UrlDecode(content:sub(idx_sp + 1))
-		end
-		local info = content:sub(1, idx_sp > 0 and idx_sp - 1 or #content)
-		local url = URL.parse("http://" .. info)
-		local params = url.query
-
-		result.alias = alias
-		result.type = "v2ray"
-		result.v2ray_protocol = "shadowsocks"
-		result.server = url.host
-		result.server_port = url.port
-
-		-- 判断 @ 前部分是否为 Base64
-		local is_base64_decoded = base64Decode(UrlDecode(url.user))
-		if is_base64_decoded:find(":") then
-        	-- 新格式:method:password
-        	result.encrypt_method_ss, result.password = is_base64_decoded:match("^(.-):(.*)$")
-		else
-        	-- 旧格式:UUID 直接作为密码
-        	result.password = url.user
-        	result.encrypt_method_ss = params.encryption or "none"
-		end
-
-		result.transport = params.type or "raw"
-		if result.transport == "tcp" then
-			result.transport = "raw"
-		end
-		if result.transport == "splithttp" then
-			result.transport = "xhttp"
-		end
-		result.tls = (params.security == "tls" or params.security == "xtls") and "1" or "0"
-		if params.alpn and params.alpn ~= "" then
-			local alpn = {}
-			for v in params.alpn:gmatch("[^,]+") do
+			for v in params.alpn:gmatch("[^,;|%s]+") do
 				table.insert(alpn, v)
 			end
 			result.tls_alpn = alpn
@@ -1181,47 +1148,53 @@ local execute = function()
 					-- 其他 base64 格式
 					else
 						-- ssd 外的格式
-						nodes = split(base64Decode(raw):gsub(" ", "_"), "\n")
+						nodes = split(base64Decode(raw):gsub("\r\n", "\n"), "\n")
 					end
 					for _, v in ipairs(nodes) do
-						if v then
-							local result
-							if szType then
-								result = processData(szType, v)
-							elseif not szType then
-								local node = trim(v)
-								local dat = split(node, "://")
-								if dat and dat[1] and dat[2] then
-									local dat3 = ""
-									if dat[3] then
-										dat3 = "://" .. dat[3]
+						if v and not string.match(v, "^%s*$") then
+							xpcall(function()
+								local result
+								if szType then
+									result = processData(szType, v)
+								elseif not szType then
+									local node = trim(v)
+									-- 一些奇葩的链接用"&amp;"、"&lt;"当做"&","#"前后带空格
+									local link = node:gsub("&[a-zA-Z]+;", "&"):gsub("%s*#%s*", "#")
+									local dat = split(link, "://")
+									if dat and dat[1] and dat[2] then
+										local dat3 = ""
+										if dat[3] then
+											dat3 = "://" .. dat[3]
+										end
+										if dat[1] == 'ss' or dat[1] == 'trojan' then
+											result = processData(dat[1], dat[2] .. dat3)
+										else
+											result = processData(dat[1], base64Decode(dat[2]))
+										end
 									end
-									if dat[1] == 'ss' or dat[1] == 'trojan' then
-										result = processData(dat[1], dat[2] .. dat3)
+								else
+									log('跳过未知类型: ' .. szType)
+								end
+								-- log(result)
+								if result then
+									-- 中文做地址的 也没有人拿中文域名搞,就算中文域也有Puny Code SB 机场
+									if not result.server or not result.server_port
+										or result.alias == "NULL"
+										or check_filer(result)
+										or result.server:match("[^0-9a-zA-Z%-_%.%s]")
+										or cache[groupHash][result.hashkey]
+									then
+										log('丢弃无效节点: ' .. result.alias)
 									else
-										result = processData(dat[1], base64Decode(dat[2]))
+										-- log('成功解析: ' .. result.type ..' 节点, ' .. result.alias)
+										result.grouphashkey = groupHash
+										tinsert(nodeResult[index], result)
+										cache[groupHash][result.hashkey] = nodeResult[index][#nodeResult[index]]
 									end
 								end
-							else
-								log('跳过未知类型: ' .. szType)
-							end
-							-- log(result)
-							if result then
-								-- 中文做地址的 也没有人拿中文域名搞,就算中文域也有Puny Code SB 机场
-								if not result.server or not result.server_port
-									or result.alias == "NULL"
-									or check_filer(result)
-									or result.server:match("[^0-9a-zA-Z%-_%.%s]")
-									or cache[groupHash][result.hashkey]
-								then
-									log('丢弃无效节点: ' .. result.alias)
-								else
-									-- log('成功解析: ' .. result.type ..' 节点, ' .. result.alias)
-									result.grouphashkey = groupHash
-									tinsert(nodeResult[index], result)
-									cache[groupHash][result.hashkey] = nodeResult[index][#nodeResult[index]]
-								end
-							end
+							end, function(err)
+								log(string.format("解析节点出错: %s\n原始数据: %s", tostring(err), tostring(v)))
+							end)	
 						end
 					end
 					log('成功解析节点数量: ' .. #nodes)
@@ -1304,7 +1277,7 @@ local execute = function()
 				luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &")
 			end
 		end
-		log('新增节点数量: ' .. add, '删除节点数量: ' .. del)
+		log('新增节点数量: ' .. add .. ', 删除节点数量: ' .. del)
 		log('订阅更新成功')
 	end
 end