diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua index 96d5c5ad6..8bdc19960 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua @@ -17,7 +17,7 @@ local function option_name(name) return option_prefix .. name end -local x_ss_encrypt_method_list = { +local ss_method_list = { "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" } @@ -298,16 +298,16 @@ o.default = "none" o:value("none") o:depends({ [option_name("protocol")] = "vless" }) -o = s:option(ListValue, option_name("x_ss_encrypt_method"), translate("Encrypt Method")) +o = s:option(ListValue, option_name("ss_method"), translate("Encrypt Method")) o.rewrite_option = "method" -for a, t in ipairs(x_ss_encrypt_method_list) do o:value(t) end +for a, t in ipairs(ss_method_list) do o:value(t) end o:depends({ [option_name("protocol")] = "shadowsocks" }) o = s:option(Flag, option_name("iv_check"), translate("IV Check")) -o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "aes-128-gcm" }) -o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "aes-256-gcm" }) -o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "chacha20-poly1305" }) -o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("x_ss_encrypt_method")] = "xchacha20-poly1305" }) +o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("ss_method")] = "aes-128-gcm" }) +o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("ss_method")] = "aes-256-gcm" }) +o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("ss_method")] = "chacha20-poly1305" }) +o:depends({ [option_name("protocol")] = "shadowsocks", [option_name("ss_method")] = "xchacha20-poly1305" }) o = s:option(Flag, option_name("uot"), translate("UDP over TCP")) o:depends({ [option_name("protocol")] = "shadowsocks" }) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua index 1b5c14df2..8450e3060 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua @@ -364,13 +364,17 @@ o:depends({ [option_name("protocol")] = "vmess" }) o:depends({ [option_name("protocol")] = "vless" }) o:depends({ [option_name("protocol")] = "http" }) o:depends({ [option_name("protocol")] = "trojan" }) +o:depends({ [option_name("protocol")] = "shadowsocks" }) o = s:option(ListValue, option_name("alpn"), translate("alpn")) o.default = "default" o:value("default", translate("Default")) -o:value("h2,http/1.1") +o:value("h3") o:value("h2") +o:value("h3,h2") o:value("http/1.1") +o:value("h2,http/1.1") +o:value("h3,h2,http/1.1") o:depends({ [option_name("tls")] = true }) o = s:option(Value, option_name("tls_serverName"), translate("Domain")) @@ -378,6 +382,7 @@ o:depends({ [option_name("tls")] = true }) o:depends({ [option_name("protocol")] = "hysteria"}) o:depends({ [option_name("protocol")] = "tuic" }) o:depends({ [option_name("protocol")] = "hysteria2" }) +o:depends({ [option_name("protocol")] = "shadowsocks" }) o = s:option(Flag, option_name("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) o.default = "0" @@ -385,6 +390,7 @@ o:depends({ [option_name("tls")] = true }) o:depends({ [option_name("protocol")] = "hysteria"}) o:depends({ [option_name("protocol")] = "tuic" }) o:depends({ [option_name("protocol")] = "hysteria2" }) +o:depends({ [option_name("protocol")] = "shadowsocks" }) if singbox_tags:find("with_ech") then o = s:option(Flag, option_name("ech"), translate("ECH")) diff --git a/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm b/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm index a84e5bba8..b0c1796ce 100644 --- a/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm +++ b/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm @@ -200,6 +200,63 @@ local api = require "luci.passwall.api" } params += "&plugin=" + encodeURIComponent(v_plugin); } + } else { + var v_transport = opt.get(dom_prefix + "transport").value; + 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 === "h2") { + v_transport = "http"; + params += opt.query("host", dom_prefix + "h2_host"); + params += opt.query("path", dom_prefix + "h2_path"); + } else if (v_transport === "tcp") { + params += opt.query("headerType", dom_prefix + "tcp_guise"); + params += opt.query("host", dom_prefix + "tcp_guise_http_host"); + params += opt.query("path", dom_prefix + "tcp_guise_http_path"); + } else if (v_transport === "mkcp") { + v_transport = "kcp"; + params += opt.query("headerType", dom_prefix + "mkcp_guise"); + } else if (v_transport === "quic") { + params += opt.query("headerType", dom_prefix + "quic_guise"); + params += opt.query("key", dom_prefix + "quic_key"); + params += opt.query("quicSecurity", dom_prefix + "quic_security"); + } else if (v_transport === "grpc") { + params += opt.query("path", dom_prefix + "grpc_serviceName"); + params += opt.query("serviceName", dom_prefix + "grpc_serviceName"); + params += opt.query("mode", dom_prefix + "grpc_mode"); + } + params += "&type=" + v_transport; + + params += opt.query("encryption", dom_prefix + "encryption"); + + if (opt.get(dom_prefix + "tls").checked) { + var v_security = "tls"; + if (opt.get(dom_prefix + "fingerprint") && opt.get(dom_prefix + "fingerprint").value != "") { + let v_fp = opt.get(dom_prefix + "fingerprint").value; + params += "&fp=" + v_fp; + } + if (opt.get(dom_prefix + "reality") && opt.get(dom_prefix + "reality").checked) { + v_security = "reality"; + if (opt.get(dom_prefix + "fingerprint") && opt.get(dom_prefix + "fingerprint").value != "") { + let v_fp = opt.get(dom_prefix + "fingerprint").value; + params += "&fp=" + v_fp; + } + params += opt.query("pbk", dom_prefix + "reality_publicKey"); + params += opt.query("sid", dom_prefix + "reality_shortId"); + params += opt.query("spx", dom_prefix + "reality_spiderX"); + } + if (opt.get(dom_prefix + "flow") && opt.get(dom_prefix + "flow").value) { + let v_flow = opt.get(dom_prefix + "flow").value; + params += "&flow=" + v_flow; + } + params += "&security=" + v_security; + params += opt.query("alpn", dom_prefix + "alpn"); + params += opt.query("sni", dom_prefix + "tls_serverName"); + } } params += "&group=" params += "#" + encodeURIComponent(v_alias.value); @@ -357,38 +414,61 @@ local api = require "luci.passwall.api" "@" + _address + ":" + v_port.value + "/?"; var params = ""; - if (opt.get(dom_prefix + "tls").checked) { - params += opt.query("sni", dom_prefix + "tls_serverName"); - params += "&security=tls" - params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure"); + var v_transport = opt.get(dom_prefix + "transport").value; + 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 === "h2") { + v_transport = "http"; + params += opt.query("host", dom_prefix + "h2_host"); + params += opt.query("path", dom_prefix + "h2_path"); + } else if (v_transport === "tcp") { + params += opt.query("headerType", dom_prefix + "tcp_guise"); + params += opt.query("host", dom_prefix + "tcp_guise_http_host"); + params += opt.query("path", dom_prefix + "tcp_guise_http_path"); + } else if (v_transport === "mkcp") { + v_transport = "kcp"; + params += opt.query("headerType", dom_prefix + "mkcp_guise"); + } else if (v_transport === "quic") { + params += opt.query("headerType", dom_prefix + "quic_guise"); + params += opt.query("key", dom_prefix + "quic_key"); + params += opt.query("quicSecurity", dom_prefix + "quic_security"); + } else if (v_transport === "grpc") { + params += opt.query("path", dom_prefix + "grpc_serviceName"); + params += opt.query("serviceName", dom_prefix + "grpc_serviceName"); + params += opt.query("mode", dom_prefix + "grpc_mode"); } - // 获取transport参数并设置type - var transport = opt.get(dom_prefix + "transport").value || ""; - switch (transport.toLowerCase()) { - case 'tcp': - params += "&type=tcp"; - break; - case 'ws': - params += "&type=ws"; - break; - case 'kcp': - params += "&type=kcp"; - break; - case 'mkcp': - params += "&type=kcp"; - break; - case 'http': - params += "&type=http"; - break; - case 'h2': - params += "&type=h2"; - break; - case 'grpc': - params += "&type=grpc"; - break; - default: - // 默认不添加type参数 - break; + params += "&type=" + v_transport; + + params += opt.query("encryption", dom_prefix + "encryption"); + + if (opt.get(dom_prefix + "tls").checked) { + var v_security = "tls"; + if (opt.get(dom_prefix + "fingerprint") && opt.get(dom_prefix + "fingerprint").value != "") { + let v_fp = opt.get(dom_prefix + "fingerprint").value; + params += "&fp=" + v_fp; + } + if (opt.get(dom_prefix + "reality") && opt.get(dom_prefix + "reality").checked) { + v_security = "reality"; + if (opt.get(dom_prefix + "fingerprint") && opt.get(dom_prefix + "fingerprint").value != "") { + let v_fp = opt.get(dom_prefix + "fingerprint").value; + params += "&fp=" + v_fp; + } + params += opt.query("pbk", dom_prefix + "reality_publicKey"); + params += opt.query("sid", dom_prefix + "reality_shortId"); + params += opt.query("spx", dom_prefix + "reality_spiderX"); + } + if (opt.get(dom_prefix + "flow") && opt.get(dom_prefix + "flow").value) { + let v_flow = opt.get(dom_prefix + "flow").value; + params += "&flow=" + v_flow; + } + params += "&security=" + v_security; + params += opt.query("alpn", dom_prefix + "alpn"); + params += opt.query("sni", dom_prefix + "tls_serverName"); } params += "#" + encodeURI(v_alias.value); if (params[0] == "&") { @@ -614,17 +694,26 @@ local api = require "luci.passwall.api" if (sipIndex !== -1) { // SIP002 var userInfo = b64decsafe(url0.substr(0, sipIndex)); - var temp = url0.substr(sipIndex + 1).split("/?"); + var temp = url0.substr(sipIndex + 1).replace('/?', '?').split('?'); var serverInfo = temp[0].split(":"); var server = serverInfo[0]; var port = serverInfo[1]; var method, password, plugin, pluginOpts; + var queryParam = {}; if (temp[1]) { - var pluginInfo = decodeURIComponent(temp[1]); - var pluginIndex = pluginInfo.indexOf(";"); - var pluginNameInfo = pluginInfo.substr(0, pluginIndex); - plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1) - pluginOpts = pluginInfo.substr(pluginIndex + 1).split("&")[0]; + var queryArray = temp[1].split('&'); + var params; + for (var i = 0; i < queryArray.length; i++) { + params = queryArray[i].split('='); + queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || ''); + } + if (queryParam.plugin) { + var pluginInfo = decodeURIComponent(temp[1]); + var pluginIndex = pluginInfo.indexOf(";"); + var pluginNameInfo = pluginInfo.substr(0, pluginIndex); + plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1) + pluginOpts = pluginInfo.substr(pluginIndex + 1).split("&")[0]; + } } var userInfoSplitIndex = userInfo.indexOf(":"); if (userInfoSplitIndex !== -1) { @@ -652,12 +741,94 @@ local api = require "luci.passwall.api" opt.set(dom_prefix + 'port', port); opt.set(dom_prefix + 'password', password || ""); opt.set(dom_prefix + 'method', method || ""); + opt.set(dom_prefix + 'ss_method', method || ""); opt.set(dom_prefix + 'plugin', plugin || "none"); if (plugin && plugin != "none") { opt.set(dom_prefix + 'plugin_opts', pluginOpts || ""); + opt.set(dom_prefix + 'plugin_enabled', true); } if (param !== undefined) { - opt.set('remarks', decodeURI(param)); + opt.set('remarks', decodeURIComponent(param)); + } + if (!queryParam.plugin && (dom_prefix == "singbox_" || dom_prefix == "xray_")) { + opt.set(dom_prefix + 'encryption', queryParam.encryption); + if (queryParam.security) { + if (queryParam.security == "tls") { + opt.set(dom_prefix + 'tls', true); + opt.set(dom_prefix + 'reality', false) + opt.set(dom_prefix + 'flow', queryParam.flow || ''); + opt.set(dom_prefix + 'alpn', queryParam.alpn || ''); + opt.set(dom_prefix + 'tls_serverName', queryParam.sni || ''); + opt.set(dom_prefix + 'tls_allowInsecure', true); + if (queryParam.allowinsecure === '0') { + opt.set(dom_prefix + 'tls_allowInsecure', false); + } + if (queryParam.fp && queryParam.fp.trim() != "") { + opt.set(dom_prefix + 'fingerprint', queryParam.fp); + } + } + + if (queryParam.security == "reality") { + opt.set(dom_prefix + 'tls', true); + opt.set(dom_prefix + 'reality', true) + opt.set(dom_prefix + 'flow', queryParam.flow || ''); + opt.set(dom_prefix + 'tls_serverName', queryParam.sni || ''); + if (queryParam.fp && queryParam.fp.trim() != "") { + 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 + 'reality_spiderX', queryParam.spx || ''); + } + + } + + queryParam.type = queryParam.type.toLowerCase(); + if (queryParam.type === "kcp" || queryParam.type === "mkcp") + queryParam.type = "mkcp" + if (queryParam.type === "h2" || queryParam.type === "http") + queryParam.type = "h2" + opt.set(dom_prefix + 'transport', queryParam.type); + if (queryParam.type === "tcp") { + opt.set(dom_prefix + 'tcp_guise', queryParam.headerType || "none"); + if (queryParam.headerType && queryParam.headerType != "none") { + opt.set(dom_prefix + 'tcp_guise_http_host', queryParam.host || ""); + opt.set(dom_prefix + 'tcp_guise_http_path', queryParam.path || ""); + } + } 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") { + opt.set(dom_prefix + 'h2_host', queryParam.host || ""); + opt.set(dom_prefix + 'h2_path', queryParam.path || ""); + } else if (queryParam.type === "quic") { + opt.set(dom_prefix + 'quic_guise', queryParam.headerType || "none"); + opt.set(dom_prefix + 'quic_security', queryParam.quicSecurity); + opt.set(dom_prefix + 'quic_key', queryParam.key); + } else if (queryParam.type === "kcp" || queryParam.type === "mkcp") { + opt.set(dom_prefix + 'mkcp_guise', queryParam.headerType || "none"); + } else if (queryParam.type === "grpc") { + opt.set(dom_prefix + 'grpc_serviceName', (queryParam.serviceName || queryParam.path) || ""); + opt.set(dom_prefix + 'grpc_mode', queryParam.mode); + } } } else { if (has_singbox) { @@ -680,10 +851,11 @@ local api = require "luci.passwall.api" opt.set(dom_prefix + 'port', part2[1]); opt.set(dom_prefix + 'password', part1[1]); opt.set(dom_prefix + 'method', part1[0]); + opt.set(dom_prefix + 'ss_method', part1[0]); opt.set(dom_prefix + 'plugin', "none"); //opt.set(dom_prefix + 'plugin_opts', ""); if (param !== undefined) { - opt.set('remarks', decodeURI(param)); + opt.set('remarks', decodeURIComponent(param)); } } } @@ -708,7 +880,7 @@ local api = require "luci.passwall.api" } var queryParam = {}; if (m.search.length > 1) { - var query = m.search.split('?'); + var query = m.search.replace('/?', '?').split('?'); var queryParams = query[1]; var queryArray = queryParams.split('&'); var params; @@ -720,38 +892,56 @@ local api = require "luci.passwall.api" opt.set(dom_prefix + 'address', m.hostname); opt.set(dom_prefix + 'port', m.port || "443"); opt.set(dom_prefix + 'password', decodeURIComponent(password)); - opt.set(dom_prefix + 'tls', queryParam.security === "tls" ? "1" : "0"); - opt.set(dom_prefix + 'tls_serverName', queryParam.peer || queryParam.sni || ''); - opt.set(dom_prefix + 'tls_allowInsecure', queryParam.allowinsecure === '1'); - // 根据type参数设置transport - var transportType = queryParam.type || ""; - switch (transportType.toLowerCase()) { - case 'tcp': - opt.set(dom_prefix + 'transport', 'tcp'); - break; - case 'ws': - opt.set(dom_prefix + 'transport', 'ws'); - break; - case 'kcp': - opt.set(dom_prefix + 'transport', 'mkcp'); - break; - case 'mkcp': - opt.set(dom_prefix + 'transport', 'mkcp'); - break; - case 'http': - opt.set(dom_prefix + 'transport', 'http'); - break; - case 'h2': - opt.set(dom_prefix + 'transport', 'h2'); - break; - case 'grpc': - opt.set(dom_prefix + 'transport', 'grpc'); - break; - default: - opt.set(dom_prefix + 'transport', ''); // 默认清空transport + + queryParam.type = queryParam.type.toLowerCase(); + if (queryParam.type === "kcp" || queryParam.type === "mkcp") + queryParam.type = "mkcp" + if (queryParam.type === "h2" || queryParam.type === "http") + queryParam.type = "h2" + opt.set(dom_prefix + 'transport', queryParam.type); + if (queryParam.type === "tcp") { + opt.set(dom_prefix + 'tcp_guise', queryParam.headerType || "none"); + if (queryParam.headerType && queryParam.headerType != "none") { + opt.set(dom_prefix + 'tcp_guise_http_host', queryParam.host || ""); + opt.set(dom_prefix + 'tcp_guise_http_path', queryParam.path || ""); + } + } 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") { + opt.set(dom_prefix + 'h2_host', queryParam.host || ""); + opt.set(dom_prefix + 'h2_path', queryParam.path || ""); + } else if (queryParam.type === "quic") { + opt.set(dom_prefix + 'quic_guise', queryParam.headerType || "none"); + opt.set(dom_prefix + 'quic_security', queryParam.quicSecurity); + opt.set(dom_prefix + 'quic_key', queryParam.key); + } else if (queryParam.type === "kcp" || queryParam.type === "mkcp") { + opt.set(dom_prefix + 'mkcp_guise', queryParam.headerType || "none"); + } else if (queryParam.type === "grpc") { + opt.set(dom_prefix + 'grpc_serviceName', (queryParam.serviceName || queryParam.path) || ""); + opt.set(dom_prefix + 'grpc_mode', queryParam.mode); } + if (m.hash) { - opt.set('remarks', decodeURI(m.hash.substr(1))); + opt.set('remarks', decodeURIComponent(m.hash.substr(1))); } } if (ssu[0] === "vmess") { @@ -802,7 +992,7 @@ local api = require "luci.passwall.api" 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('&'); + 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] || ''); @@ -848,7 +1038,7 @@ local api = require "luci.passwall.api" opt.set(dom_prefix + 'port', m.port || "443"); var queryParam = {}; if (m.search.length > 1) { - var query = m.search.split('?'); + var query = m.search.replace('/?', '?').split('?') var queryParams = query[1]; var queryArray = queryParams.split('&'); var params; @@ -910,7 +1100,7 @@ local api = require "luci.passwall.api" 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('&'); + 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] || ''); @@ -941,7 +1131,7 @@ local api = require "luci.passwall.api" } if (m.hash) { - opt.set('remarks', decodeURI(m.hash.substr(1))); + opt.set('remarks', decodeURIComponent(m.hash.substr(1))); } } if (ssu[0] === "hysteria2" || ssu[0] === "hy2") { @@ -953,7 +1143,7 @@ local api = require "luci.passwall.api" } var queryParam = {}; if (m.search.length > 1) { - var query = m.search.split('?'); + var query = m.search.replace('/?', '?').split('?') var queryParams = query[1]; var queryArray = queryParams.split('&'); var params; @@ -992,7 +1182,7 @@ local api = require "luci.passwall.api" opt.set(dom_prefix + 'tls_allowInsecure', true); } if (m.hash) { - opt.set('remarks', decodeURI(m.hash.substr(1))); + opt.set('remarks', decodeURIComponent(m.hash.substr(1))); } } if (dom_prefix && dom_prefix != null) { diff --git a/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/luci-app-passwall/root/usr/share/passwall/subscribe.lua index 5421327fc..1d22a1ad5 100755 --- a/luci-app-passwall/root/usr/share/passwall/subscribe.lua +++ b/luci-app-passwall/root/usr/share/passwall/subscribe.lua @@ -505,6 +505,7 @@ local function processData(szType, content, add_mode, add_from) --ss://cmM0LW1kNTpwYXNzd2Q@192.168.100.1:8888/?plugin=obfs-local%3Bobfs%3Dhttp#Example2 --ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888#Example3 --ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888/?plugin=v2ray-plugin%3Bserver#Example3 + --ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTp0ZXN0@xxxxxx.com:443?type=ws&path=%2Ftestpath&host=xxxxxx.com&security=tls&fp=&alpn=h3%2Ch2%2Chttp%2F1.1&sni=xxxxxx.com#test-1%40ss local idx_sp = 0 local alias = "" @@ -513,17 +514,17 @@ local function processData(szType, content, add_mode, add_from) alias = content:sub(idx_sp + 1, -1) end result.remarks = UrlDecode(alias) - local info = content:sub(1, idx_sp - 1) - if info:find("/%?") then - local find_index = info:find("/%?") - local query = split(info, "/%?") - local params = {} + local info = content:sub(1, idx_sp - 1):gsub("/%?", "?") + local params = {} + if info:find("?") then + local find_index = info:find("?") + local query = split(info, "?") for _, v in pairs(split(query[2], '&')) do local t = split(v, '=') - params[t[1]] = t[2] + params[t[1]] = UrlDecode(t[2]) end if params.plugin then - local plugin_info = UrlDecode(params.plugin) + local plugin_info = params.plugin local idx_pn = plugin_info:find(";") if idx_pn then result.plugin = plugin_info:sub(1, idx_pn - 1) @@ -539,7 +540,7 @@ local function processData(szType, content, add_mode, add_from) info = info:sub(1, find_index - 1) end - local hostInfo = split(base64Decode(info), "@") + local hostInfo = split(UrlDecode(info), "@") if hostInfo and #hostInfo > 0 then local host_port = hostInfo[#hostInfo] -- [2001:4860:4860::8888]:443 @@ -616,6 +617,85 @@ local function processData(szType, content, add_mode, add_from) result.error_msg = "shadowsocks-libev 不支持2022加密." end end + + if params.type then + params.type = string.lower(params.type) + result.transport = params.type + if result.type ~= "SS-Rust" and result.type ~= "SS" then + 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 == 'h2' or params.type == 'http' then + if result.type == "sing-box" then + result.transport = "http" + result.http_host = params.host + result.http_path = params.path + elseif result.type == "xray" then + result.transport = "h2" + result.h2_host = params.host + result.h2_path = params.path + end + end + if params.type == 'tcp' then + result.tcp_guise = params.headerType or "none" + result.tcp_guise_http_host = params.host + result.tcp_guise_http_path = params.path + end + if params.type == 'kcp' or params.type == 'mkcp' then + result.transport = "mkcp" + result.mkcp_guise = params.headerType or "none" + result.mkcp_mtu = 1350 + result.mkcp_tti = 50 + result.mkcp_uplinkCapacity = 5 + result.mkcp_downlinkCapacity = 20 + result.mkcp_readBufferSize = 2 + result.mkcp_writeBufferSize = 2 + result.mkcp_seed = params.seed + end + if params.type == 'quic' then + result.quic_guise = params.headerType or "none" + result.quic_key = params.key + result.quic_security = params.quicSecurity or "none" + end + if params.type == 'grpc' then + if params.path then result.grpc_serviceName = params.path end + if params.serviceName then result.grpc_serviceName = params.serviceName end + result.grpc_mode = params.mode + end + result.tls = "0" + if params.security == "tls" or params.security == "reality" then + result.tls = "1" + result.tls_serverName = (params.sni and params.sni ~= "") and params.sni or params.host + result.alpn = params.alpn + result.fingerprint = (params.fp and params.fp ~= "") and params.fp or "chrome" + if params.security == "reality" then + result.reality = "1" + result.reality_publicKey = params.pbk or nil + result.reality_shortId = params.sid or nil + result.reality_spiderX = params.spx or nil + end + end + result.tls_allowInsecure = allowInsecure_default and "1" or "0" + else + result.error_msg = "请更换Xray或Sing-Box来支持SS更多的传输方式." + end + end end elseif szType == "trojan" then if trojan_type_default == "trojan-plus" and has_trojan_plus then