diff --git a/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua b/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua index f381a54b2..eb7ccb701 100644 --- a/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua +++ b/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua @@ -21,11 +21,15 @@ function index() entry({"admin", "services", "shadowsocksr", "subscribe"}, call("subscribe")) entry({"admin", "services", "shadowsocksr", "checkport"}, call("check_port")) entry({"admin", "services", "shadowsocksr", "log"}, form("shadowsocksr/log"), _("Log"), 80).leaf = true + entry({"admin", "services", "shadowsocksr", "get_log"}, call("get_log")).leaf = true + entry({"admin", "services", "shadowsocksr", "clear_log"}, call("clear_log")).leaf = true entry({"admin", "services", "shadowsocksr", "run"}, call("act_status")) entry({"admin", "services", "shadowsocksr", "ping"}, call("act_ping")) entry({"admin", "services", "shadowsocksr", "reset"}, call("act_reset")) entry({"admin", "services", "shadowsocksr", "restart"}, call("act_restart")) entry({"admin", "services", "shadowsocksr", "delete"}, call("act_delete")) + --[[Backup]] + entry({"admin", "services", "shadowsocksr", "backup"}, call("create_backup")).leaf = true end function subscribe() @@ -107,9 +111,9 @@ function check_port() ret = socket:connect(s.server, s.server_port) if tostring(ret) == "true" then socket:close() - retstring = retstring .. "[" .. server_name .. "] OK.
" + retstring = retstring .. "[" .. server_name .. "] OK.
" else - retstring = retstring .. "[" .. server_name .. "] Error.
" + retstring = retstring .. "[" .. server_name .. "] Error.
" end if iret == 0 then luci.sys.call("ipset del ss_spec_wan_ac " .. s.server) @@ -120,7 +124,7 @@ function check_port() end function act_reset() - luci.sys.call("/etc/init.d/shadowsocksr reset &") + luci.sys.call("/etc/init.d/shadowsocksr reset >/dev/null 2>&1") luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr")) end @@ -133,3 +137,28 @@ function act_delete() luci.sys.call("/etc/init.d/shadowsocksr restart &") luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers")) end + +function get_log() + luci.http.write(luci.sys.exec("[ -f '/var/log/ssrplus.log' ] && cat /var/log/ssrplus.log")) +end + +function clear_log() + luci.sys.call("echo '' > /var/log/ssrplus.log") +end + +function create_backup() + local backup_files = { + "/etc/config/shadowsocksr", + "/etc/ssrplus/*" + } + local date = os.date("%Y%m%d") + local tar_file = "/tmp/shadowsocksr-" .. date .. "-backup.tar.gz" + nixio.fs.remove(tar_file) + local cmd = "tar -czf " .. tar_file .. " " .. table.concat(backup_files, " ") + luci.sys.call(cmd) + luci.http.header("Content-Disposition", "attachment; filename=shadowsocksr-" .. date .. "-backup.tar.gz") + luci.http.header("X-Backup-Filename", "shadowsocksr-" .. date .. "-backup.tar.gz") + luci.http.prepare_content("application/octet-stream") + luci.http.write(nixio.fs.readfile(tar_file)) + nixio.fs.remove(tar_file) +end diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua index de97787f2..e5f159343 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua @@ -1,5 +1,7 @@ +local m, s, o local uci = luci.model.uci.cursor() local server_table = {} +local type_table = {} local function is_finded(e) return luci.sys.exec('type -t -p "%s"' % e) ~= "" and true or false end @@ -10,6 +12,9 @@ uci:foreach("shadowsocksr", "servers", function(s) elseif s.server and s.server_port then server_table[s[".name"]] = "[%s]:%s:%s" % {string.upper(s.v2ray_protocol or s.type), s.server, s.server_port} end + if s.type then + type_table[s[".name"]] = s.type + end end) local key_table = {} @@ -96,7 +101,7 @@ o.default = "https://raw.githubusercontent.com/neodevpro/neodevhost/master/lite_ o:depends("adblock", "1") o.description = translate("Support AdGuardHome and DNSMASQ format list") -o = s:option(Button, "reset", translate("Reset to defaults")) +o = s:option(Button, "Reset", translate("Reset to defaults")) o.inputstyle = "reload" o.write = function() luci.sys.call("/etc/init.d/shadowsocksr reset") @@ -107,15 +112,82 @@ end s = m:section(TypedSection, "socks5_proxy", translate("Global SOCKS5 Proxy Server")) s.anonymous = true -o = s:option(ListValue, "server", translate("Server")) -o:value("nil", translate("Disable")) -o:value("same", translate("Same as Global Server")) -for _, key in pairs(key_table) do - o:value(key, server_table[key]) -end -o.default = "nil" +-- Enable/Disable Option +o = s:option(Flag, "enabled", translate("Enable")) +o.default = 0 o.rmempty = false +-- Server Selection +o = s:option(ListValue, "server", translate("Server")) +o:value("same", translate("Same as Global Server")) +for _, key in pairs(key_table) do + o:value(key, server_table[key]) +end +o.default = "same" +o.rmempty = false + +-- Dynamic value handling based on enabled/disabled state +o.cfgvalue = function(self, section) + local enabled = m:get(section, "enabled") + if enabled == "0" then + return m:get(section, "old_server") or "same" + end + return Value.cfgvalue(self, section) or "same" -- Default to `same` when enabled +end + +o.write = function(self, section, value) + local enabled = m:get(section, "enabled") + if enabled == "0" then + local old_server = Value.cfgvalue(self, section) or "same" + if old_server ~= "nil" then + m:set(section, "old_server", old_server) + end + m:set(section, "server", "nil") + else + m:del(section, "old_server") + -- Write the value normally when enabled + Value.write(self, section, value) + end +end + +-- Socks Auth +if is_finded("xray") then +o = s:option(ListValue, "socks5_auth", translate("Socks5 Auth Mode"), translate("Socks protocol auth methods, default:noauth.")) +o.default = "noauth" +o:value("noauth", "NOAUTH") +o:value("password", "PASSWORD") +o.rmempty = true +for key, server_type in pairs(type_table) do + if server_type == "v2ray" then + -- 如果服务器类型是 v2ray,则设置依赖项显示 + o:depends("server", key) + end +end + +-- Socks User +o = s:option(Value, "socks5_user", translate("Socks5 User"), translate("Only when auth is password valid, Mandatory.")) +o.rmempty = true +o:depends("socks5_auth", "password") + +-- Socks Password +o = s:option(Value, "socks5_pass", translate("Socks5 Password"), translate("Only when auth is password valid, Not mandatory.")) +o.password = true +o.rmempty = true +o:depends("socks5_auth", "password") + +-- Socks Mixed +o = s:option(Flag, "socks5_mixed", translate("Enabled Mixed"), translate("Mixed as an alias of socks, default:Enabled.")) +o.default = "1" +o.rmempty = false +for key, server_type in pairs(type_table) do + if server_type == "v2ray" then + -- 如果服务器类型是 v2ray,则设置依赖项显示 + o:depends("server", key) + end +end +end + +-- Local Port o = s:option(Value, "local_port", translate("Local Port")) o.datatype = "port" o.default = 1080 @@ -132,6 +204,7 @@ o.default = 0 o = s:option(ListValue, "fragment_packets", translate("Fragment Packets"), translate("\"1-3\" is for segmentation at TCP layer, applying to the beginning 1 to 3 data writes by the client. \"tlshello\" is for TLS client hello packet fragmentation.")) o.default = "tlshello" o:value("tlshello", "tlshello") +o:value("1-1", "1-1") o:value("1-2", "1-2") o:value("1-3", "1-3") o:value("1-5", "1-5") diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua index 2b91c2cf8..c858b9515 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua @@ -618,8 +618,7 @@ o:depends({type = "v2ray", v2ray_protocol = "socks"}) -- 传输协议 o = s:option(ListValue, "transport", translate("Transport")) -o:value("tcp", "TCP") -o:value("raw", "RAW") +o:value("raw", "RAW (TCP)") o:value("kcp", "mKCP") o:value("ws", "WebSocket") o:value("httpupgrade", "HTTPUpgrade") @@ -635,17 +634,9 @@ o:depends({type = "v2ray", v2ray_protocol = "shadowsocks"}) o:depends({type = "v2ray", v2ray_protocol = "socks"}) o:depends({type = "v2ray", v2ray_protocol = "http"}) --- [[ TCP部分 ]]-- +-- [[ RAW部分 ]]-- -- TCP伪装 o = s:option(ListValue, "tcp_guise", translate("Camouflage Type")) -o:depends("transport", "tcp") -o:value("none", translate("None")) -o:value("http", "HTTP") -o.rmempty = true - --- [[ RAW部分 ]]-- --- RAW伪装 -o = s:option(ListValue, "raw_guise", translate("Camouflage Type")) o:depends("transport", "raw") o:value("none", translate("None")) o:value("http", "HTTP") @@ -654,13 +645,11 @@ o.rmempty = true -- HTTP域名 o = s:option(Value, "http_host", translate("HTTP Host")) o:depends("tcp_guise", "http") -o:depends("raw_guise", "http") o.rmempty = true -- HTTP路径 o = s:option(Value, "http_path", translate("HTTP Path")) o:depends("tcp_guise", "http") -o:depends("raw_guise", "http") o.rmempty = true -- [[ WS部分 ]]-- @@ -935,17 +924,20 @@ if is_finded("xray") then -- [[ XTLS ]]-- o = s:option(ListValue, "tls_flow", translate("Flow")) for _, v in ipairs(tls_flows) do - o:value(v, translate(v)) + if v == "none" then + o.default = "none" + o:value("none", translate("none")) + else + o:value(v, translate(v)) + end end o.rmempty = true - o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "tcp", tls = true}) o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "raw", tls = true}) - o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "tcp", reality = true}) o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "raw", reality = true}) -- [[ uTLS ]]-- o = s:option(ListValue, "fingerprint", translate("Finger Print")) - o.default = "chrome" + o.default = "" o:value("chrome", translate("chrome")) o:value("firefox", translate("firefox")) o:value("safari", translate("safari")) @@ -1024,7 +1016,7 @@ o:depends("mux", true) -- [[ MPTCP ]]-- -o = s:option(Flag, "mptcp", translate("MPTCP")) +o = s:option(Flag, "mptcp", translate("MPTCP"), translate("Enabling MPTCP Requires Server Support.")) o.rmempty = false o.default = false o:depends({type = "v2ray", v2ray_protocol = "vless"}) diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua index 070fb5b9b..956f8959d 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua @@ -1,5 +1,6 @@ require "luci.ip" require "nixio.fs" +require "luci.sys" local m, s, o m = Map("shadowsocksr") @@ -140,4 +141,11 @@ o.remove = function(self, section, value) nixio.fs.writefile(netflixconf, "") end +if luci.sys.call('[ -f "/www/luci-static/resources/uci.js" ]') == 0 then + m.apply_on_parse = true + function m.on_apply(self) + luci.sys.call("/etc/init.d/shadowsocksr reload > /dev/null 2>&1 &") + end +end + return m diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/log.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/log.lua index fdf9e59f0..b0bd4f6f3 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/log.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/log.lua @@ -1,20 +1,102 @@ require "luci.util" require "nixio.fs" +require "luci.sys" +require "luci.http" + f = SimpleForm("logview") f.reset = false f.submit = false -t = f:field(TextValue, "conf") -t.rmempty = true -t.rows = 20 -function t.cfgvalue() - if nixio.fs.access("/var/log/ssrplus.log") then - local logs = luci.util.execi("cat /var/log/ssrplus.log") - local s = "" - for line in logs do - s = line .. "\n" .. s - end - return s - end +f:append(Template("shadowsocksr/log")) + +-- 自定义 log 函数 +function log(...) + local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ") + local f, err = io.open("/var/log/ssrplus.log", "a") + if f and err == nil then + f:write(result .. "\n") + f:close() + end end -t.readonly = "readonly" -return f + +-- 创建备份与恢复表单 +fb = SimpleForm('backup-restore') +fb.reset = false +fb.submit = false +s = fb:section(SimpleSection, translate("Backup and Restore"), translate("Backup or Restore Client and Server Configurations.") .. + "
" .. + translate("Note: Restoring configurations across different versions may cause compatibility issues.") .. + "") +o = s:option(DummyValue, '', nil) +o.template = "shadowsocksr/backup_restore" + +-- 定义备份目标文件和目录 +local backup_targets = { + files = { + "/etc/config/shadowsocksr" + }, + dirs = { + "/etc/ssrplus" + } +} + +local file_path = '/tmp/shadowsocksr_upload.tar.gz' +local temp_dir = '/tmp/shadowsocksr_bak' +local fd + +-- 处理文件上传 +luci.http.setfilehandler(function(meta, chunk, eof) + if not fd and meta and meta.name == "ulfile" and chunk then + -- 初始化上传处理 + luci.sys.call("rm -rf " .. temp_dir) + nixio.fs.remove(file_path) + fd = nixio.open(file_path, "w") + luci.sys.call("echo '' > /var/log/ssrplus.log") + end + + if fd and chunk then + fd:write(chunk) + end + + if eof and fd then + fd:close() + fd = nil + if nixio.fs.access(file_path) then + log(" * shadowsocksr 配置文件上传成功…") -- 使用自定义的 log 函数 + luci.sys.call("mkdir -p " .. temp_dir) + + if luci.sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then + -- 处理文件还原 + for _, target in ipairs(backup_targets.files) do + local temp_file = temp_dir .. target + if nixio.fs.access(temp_file) then + luci.sys.call(string.format("cp -f '%s' '%s'", temp_file, target)) + log(" * 文件 " .. target .. " 还原成功…") -- 使用自定义的 log 函数 + end + end + + -- 处理目录还原 + for _, target in ipairs(backup_targets.dirs) do + local temp_dir_path = temp_dir .. target + if nixio.fs.access(temp_dir_path) then + luci.sys.call(string.format("cp -rf '%s'/* '%s/'", temp_dir_path, target)) + log(" * 目录 " .. target .. " 还原成功…") -- 使用自定义的 log 函数 + end + end + + log(" * shadowsocksr 配置还原成功…") -- 使用自定义的 log 函数 + log(" * 重启 shadowsocksr 服务中…\n") -- 使用自定义的 log 函数 + luci.sys.call('/etc/init.d/shadowsocksr restart > /dev/null 2>&1 &') + else + log(" * shadowsocksr 配置文件解压失败,请重试!") -- 使用自定义的 log 函数 + end + else + log(" * shadowsocksr 配置文件上传失败,请重试!") -- 使用自定义的 log 函数 + end + + -- 清理临时文件 + luci.sys.call("rm -rf " .. temp_dir) + nixio.fs.remove(file_path) + end +end) + +return f, fb diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua index d1850cca7..a7166abdb 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua @@ -29,7 +29,7 @@ o:value("5", translate("Every Friday")) o:value("6", translate("Every Saturday")) o:value("0", translate("Every Sunday")) o.default = "*" -o.rmempty = false +o.rmempty = true o:depends("auto_update", "1") o = s:option(ListValue, "auto_update_day_time", translate("Update time (every day)")) @@ -37,7 +37,7 @@ for t = 0, 23 do o:value(t, t .. ":00") end o.default = 2 -o.rmempty = false +o.rmempty = true o:depends("auto_update", "1") o = s:option(ListValue, "auto_update_min_time", translate("Update Interval (min)")) @@ -45,7 +45,7 @@ for i = 0, 59 do o:value(i, i .. ":00") end o.default = 30 -o.rmempty = false +o.rmempty = true o:depends("auto_update", "1") o = s:option(DynamicList, "subscribe_url", translate("Subscribe URL")) diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua index 65b916ec0..83a88a2a4 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua @@ -155,6 +155,14 @@ if nixio.fs.access("/usr/bin/kcptun-client") then end end +s = m:field(Button, "Restart", translate("Restart ShadowSocksR Plus+")) +s.inputtitle = translate("Restart Service") +s.inputstyle = "reload" +s.write = function() + luci.sys.call("/etc/init.d/shadowsocksr restart >/dev/null 2>&1 &") + luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "client")) +end + s = m:field(DummyValue, "google", translate("Google Connectivity")) s.value = translate("No Check") s.template = "shadowsocksr/check" @@ -181,10 +189,10 @@ if uci:get_first("shadowsocksr", 'global', 'apple_optimization', '0') ~= '0' the end if uci:get_first("shadowsocksr", 'global', 'netflix_enable', '0') ~= '0' then -s = m:field(DummyValue, "nfip_data", translate("Netflix IP Data")) -s.rawhtml = true -s.template = "shadowsocksr/refresh" -s.value = nfip_count .. " " .. translate("Records") + s = m:field(DummyValue, "nfip_data", translate("Netflix IP Data")) + s.rawhtml = true + s.template = "shadowsocksr/refresh" + s.value = nfip_count .. " " .. translate("Records") end if uci:get_first("shadowsocksr", 'global', 'adblock', '0') == '1' then diff --git a/luci-app-ssr-plus/luasrc/view/shadowsocksr/backup_restore.htm b/luci-app-ssr-plus/luasrc/view/shadowsocksr/backup_restore.htm new file mode 100644 index 000000000..d775addf8 --- /dev/null +++ b/luci-app-ssr-plus/luasrc/view/shadowsocksr/backup_restore.htm @@ -0,0 +1,154 @@ +<%+cbi/valueheader%> +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + + + + + +<%+cbi/valuefooter%> diff --git a/luci-app-ssr-plus/luasrc/view/shadowsocksr/log.htm b/luci-app-ssr-plus/luasrc/view/shadowsocksr/log.htm new file mode 100644 index 000000000..251f14b22 --- /dev/null +++ b/luci-app-ssr-plus/luasrc/view/shadowsocksr/log.htm @@ -0,0 +1,37 @@ +<% +local dsp = require "luci.dispatcher" +-%> + +
+ + +
diff --git a/luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm b/luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm index a7485acb5..62e1b1cc5 100644 --- a/luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm +++ b/luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm @@ -217,8 +217,9 @@ function import_ssr_url(btn, urlname, sid) { case "trojan": try { var url = new URL("http://" + ssu[1]); + var params = url.searchParams; } catch(e) { - alert(e) + alert(e); return false; } @@ -232,7 +233,65 @@ function import_ssr_url(btn, urlname, sid) { document.getElementsByName('cbid.shadowsocksr.' + sid + '.password')[0].value = decodeURIComponent(url.username); document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls')[0].checked = true; document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls')[0].dispatchEvent(event); - document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls_host')[0].value = url.searchParams.get("sni"); + document.getElementsByName('cbid.shadowsocksr.' + sid + '.fingerprint')[0].value = params.get("fp") || ""; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls_host')[0].value = params.get("sni"); + if (params.get("allowInsecure") === "1") { + document.getElementsByName('cbid.shadowsocksr.' + sid + '.insecure')[0].checked = true; // 设置 insecure 为 true + document.getElementsByName('cbid.shadowsocksr.' + sid + '.insecure')[0].dispatchEvent(event); // 触发事件 + } + document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport')[0].value = + params.get("type") == "http" ? "h2" : + (["tcp", "raw"].includes(params.get("type")) ? "raw" : + (params.get("type") || "raw")); + document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport')[0].dispatchEvent(event); + switch (params.get("type")) { + case "ws": + if (params.get("security") !== "tls") { + setElementValue('cbid.shadowsocksr.' + sid + '.ws_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.shadowsocksr.' + sid + '.ws_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); + break; + case "httpupgrade": + if (params.get("security") !== "tls") { + document.getElementsByName('cbid.shadowsocksr.' + sid + '.httpupgrade_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + } + document.getElementsByName('cbid.shadowsocksr.' + sid + '.httpupgrade_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + break; + case "splithttp": + if (params.get("security") !== "tls") { + document.getElementsByName('cbid.shadowsocksr.' + sid + '.splithttp_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + } + document.getElementsByName('cbid.shadowsocksr.' + sid + '.splithttp_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + break; + case "kcp": + document.getElementsByName('cbid.shadowsocksr.' + sid + '.kcp_guise')[0].value = params.get("headerType") || "none"; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.seed')[0].value = params.get("seed") || ""; + break; + case "http": + /* this is non-standard, bullshit */ + case "h2": + document.getElementsByName('cbid.shadowsocksr.' + sid + '.h2_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.h2_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; + break; + case "quic": + document.getElementsByName('cbid.shadowsocksr.' + sid + '.quic_guise')[0].value = params.get("headerType") || "none"; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.quic_security')[0].value = params.get("quicSecurity") || "none"; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.quic_key')[0].value = params.get("key") || ""; + break; + case "grpc": + document.getElementsByName('cbid.shadowsocksr.' + sid + '.serviceName')[0].value = params.get("serviceName") || ""; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.grpc_mode')[0].value = params.get("mode") || "gun"; + break; + case "raw": + case "tcp": + document.getElementsByName('cbid.shadowsocksr.' + sid + '.tcp_guise')[0].value = params.get("headerType") || "none"; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.tcp_guise')[0].dispatchEvent(event); + if (params.get("headerType") === "http") { + document.getElementsByName('cbid.shadowsocksr.' + sid + '.http_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.http_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; + } + break; + } s.innerHTML = "<%:Import configuration information successfully.%>"; return false; @@ -254,11 +313,12 @@ function import_ssr_url(btn, urlname, sid) { document.getElementsByName('cbid.shadowsocksr.' + sid + '.server_port')[0].value = ssm.port; document.getElementsByName('cbid.shadowsocksr.' + sid + '.alter_id')[0].value = ssm.aid; document.getElementsByName('cbid.shadowsocksr.' + sid + '.vmess_id')[0].value = ssm.id; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport')[0].value = ssm.net; + document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport')[0].value = + (ssm.net === "raw" || ssm.net === "tcp") ? "raw" : ssm.net; document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport')[0].dispatchEvent(event); - if (ssm.net == "tcp") { + if (ssm.net === "raw" || ssm.net === "tcp") { if (ssm.type && ssm.type != "http") { - ssm.type = "none" + ssm.type = "none"; } else { document.getElementsByName('cbid.shadowsocksr.' + sid + '.http_host')[0].value = ssm.host; document.getElementsByName('cbid.shadowsocksr.' + sid + '.http_path')[0].value = ssm.path; @@ -294,8 +354,10 @@ function import_ssr_url(btn, urlname, sid) { document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls')[0].dispatchEvent(event); document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls_host')[0].value = ssm.sni || ssm.host; } + if (ssm.mux !== undefined) { document.getElementsByName('cbid.shadowsocksr.' + sid + '.mux')[0].checked = true; document.getElementsByName('cbid.shadowsocksr.' + sid + '.mux')[0].dispatchEvent(event); + } s.innerHTML = "<%:Import configuration information successfully.%>"; return false; case "vless": @@ -303,88 +365,101 @@ function import_ssr_url(btn, urlname, sid) { var url = new URL("http://" + ssu[1]); var params = url.searchParams; } catch(e) { - alert(e) + alert(e); return false; } - - document.getElementsByName('cbid.shadowsocksr.' + sid + '.alias')[0].value = url.hash ? decodeURIComponent(url.hash.slice(1)) : ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = "v2ray"; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].dispatchEvent(event); - document.getElementsByName('cbid.shadowsocksr.' + sid + '.v2ray_protocol')[0].value = "vless"; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.v2ray_protocol')[0].dispatchEvent(event); - document.getElementsByName('cbid.shadowsocksr.' + sid + '.server')[0].value = url.hostname; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.server_port')[0].value = url.port || "80"; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.vmess_id')[0].value = url.username; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport')[0].value = - params.get("type") == "http" ? "h2" : - (params.get("type") == "raw" ? "raw" : - (params.get("type") || "tcp")); - document.getElementsByName('cbid.shadowsocksr.' + sid + '.transport')[0].dispatchEvent(event); - document.getElementsByName('cbid.shadowsocksr.' + sid + '.vless_encryption')[0].value = params.get("encryption") || "none"; + // Check if the elements exist before trying to modify them + function setElementValue(name, value) { + const element = document.getElementsByName(name)[0]; + if (element) { + if (element.type === "checkbox" || element.type === "radio") { + element.checked = value === true; + } else { + element.value = value; + } + } + } + function dispatchEventIfExists(name, event) { + const element = document.getElementsByName(name)[0]; + if (element) { + element.dispatchEvent(event); + } + } + setElementValue('cbid.shadowsocksr.' + sid + '.alias', url.hash ? decodeURIComponent(url.hash.slice(1)) : ""); + setElementValue('cbid.shadowsocksr.' + sid + '.type', "v2ray"); + dispatchEventIfExists('cbid.shadowsocksr.' + sid + '.type', event); + setElementValue('cbid.shadowsocksr.' + sid + '.v2ray_protocol', "vless"); + dispatchEventIfExists('cbid.shadowsocksr.' + sid + '.v2ray_protocol', event); + setElementValue('cbid.shadowsocksr.' + sid + '.server', url.hostname); + setElementValue('cbid.shadowsocksr.' + sid + '.server_port', url.port || "80"); + setElementValue('cbid.shadowsocksr.' + sid + '.vmess_id', url.username); + setElementValue('cbid.shadowsocksr.' + sid + '.transport', + params.get("type") === "http" ? "h2" : + (["tcp", "raw"].includes(params.get("type")) ? "raw" : + (params.get("type") || "tcp")) + ); + dispatchEventIfExists('cbid.shadowsocksr.' + sid + '.transport', event); + setElementValue('cbid.shadowsocksr.' + sid + '.vless_encryption', params.get("encryption") || "none"); if ([ "tls", "xtls", "reality" ].includes(params.get("security"))) { - document.getElementsByName('cbid.shadowsocksr.' + sid + '.' + params.get("security"))[0].checked = true; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.' + params.get("security"))[0].dispatchEvent(event); + setElementValue('cbid.shadowsocksr.' + sid + '.' + params.get("security"), true); + dispatchEventIfExists('cbid.shadowsocksr.' + sid + '.' + params.get("security"), event); if (params.get("security") === "reality") { - document.getElementsByName('cbid.shadowsocksr.' + sid + '.reality_publickey')[0].value = params.get("pbk") ? decodeURIComponent(params.get("pbk")) : ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.reality_shortid')[0].value = params.get("sid") || ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.reality_spiderx')[0].value = params.get("spx") ? decodeURIComponent(params.get("spx")) : ""; + setElementValue('cbid.shadowsocksr.' + sid + '.reality_publickey', params.get("pbk") ? decodeURIComponent(params.get("pbk")) : ""); + setElementValue('cbid.shadowsocksr.' + sid + '.reality_shortid', params.get("sid") || ""); + setElementValue('cbid.shadowsocksr.' + sid + '.reality_spiderx', params.get("spx") ? decodeURIComponent(params.get("spx")) : ""); } - if (params.get("security") === "xtls") { - document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls_flow')[0].value = params.get("flow") || ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls_flow')[0].dispatchEvent(event); - } - document.getElementsByName('cbid.shadowsocksr.' + sid + '.fingerprint')[0].value = params.get("fp") || ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls_host')[0].value = params.get("sni") || ""; + setElementValue('cbid.shadowsocksr.' + sid + '.tls_flow', params.get("flow") || "none"); + dispatchEventIfExists('cbid.shadowsocksr.' + sid + '.tls_flow', event); + + setElementValue('cbid.shadowsocksr.' + sid + '.fingerprint', params.get("fp") || ""); + setElementValue('cbid.shadowsocksr.' + sid + '.tls_host', params.get("sni") || ""); } switch (params.get("type")) { case "ws": - if (params.get("security") !== "tls") - document.getElementsByName('cbid.shadowsocksr.' + sid + '.ws_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.ws_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + if (params.get("security") !== "tls") { + setElementValue('cbid.shadowsocksr.' + sid + '.ws_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.shadowsocksr.' + sid + '.ws_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); break; case "httpupgrade": - if (params.get("security") !== "tls") - document.getElementsByName('cbid.shadowsocksr.' + sid + '.httpupgrade_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.httpupgrade_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + if (params.get("security") !== "tls") { + setElementValue('cbid.shadowsocksr.' + sid + '.httpupgrade_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.shadowsocksr.' + sid + '.httpupgrade_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); break; case "splithttp": - if (params.get("security") !== "tls") - document.getElementsByName('cbid.shadowsocksr.' + sid + '.splithttp_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.splithttp_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + if (params.get("security") !== "tls") { + setElementValue('cbid.shadowsocksr.' + sid + '.splithttp_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.shadowsocksr.' + sid + '.splithttp_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); break; case "kcp": - document.getElementsByName('cbid.shadowsocksr.' + sid + '.kcp_guise')[0].value = params.get("headerType") || "none"; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.seed')[0].value = params.get("seed") || ""; + setElementValue('cbid.shadowsocksr.' + sid + '.kcp_guise', params.get("headerType") || "none"); + setElementValue('cbid.shadowsocksr.' + sid + '.seed', params.get("seed") || ""); break; case "http": /* this is non-standard, bullshit */ case "h2": - document.getElementsByName('cbid.shadowsocksr.' + sid + '.h2_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.h2_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; + setElementValue('cbid.shadowsocksr.' + sid + '.h2_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + setElementValue('cbid.shadowsocksr.' + sid + '.h2_path', params.get("path") ? decodeURIComponent(params.get("path")) : ""); break; case "quic": - document.getElementsByName('cbid.shadowsocksr.' + sid + '.quic_guise')[0].value = params.get("headerType") || "none"; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.quic_security')[0].value = params.get("quicSecurity") || "none"; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.quic_key')[0].value = params.get("key") || ""; + setElementValue('cbid.shadowsocksr.' + sid + '.quic_guise', params.get("headerType") || "none"); + setElementValue('cbid.shadowsocksr.' + sid + '.quic_security', params.get("quicSecurity") || "none"); + setElementValue('cbid.shadowsocksr.' + sid + '.quic_key', params.get("key") || ""); break; case "grpc": - document.getElementsByName('cbid.shadowsocksr.' + sid + '.serviceName')[0].value = params.get("serviceName") || ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.grpc_mode')[0].value = params.get("mode") || "gun"; + setElementValue('cbid.shadowsocksr.' + sid + '.serviceName', params.get("serviceName") || ""); + setElementValue('cbid.shadowsocksr.' + sid + '.grpc_mode', params.get("mode") || "gun"); break; case "tcp": - document.getElementsByName('cbid.shadowsocksr.' + sid + '.tcp_guise')[0].value = params.get("headerType") || "none"; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.tcp_guise')[0].dispatchEvent(event); - if (params.get("headerType") === "http") { - document.getElementsByName('cbid.shadowsocksr.' + sid + '.http_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.http_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; - } case "raw": - document.getElementsByName('cbid.shadowsocksr.' + sid + '.raw_guise')[0].value = params.get("headerType") || "none"; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.raw_guise')[0].dispatchEvent(event); + setElementValue('cbid.shadowsocksr.' + sid + '.tcp_guise', params.get("headerType") || "none"); + dispatchEventIfExists('cbid.shadowsocksr.' + sid + '.tcp_guise', event); if (params.get("headerType") === "http") { - document.getElementsByName('cbid.shadowsocksr.' + sid + '.http_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.shadowsocksr.' + sid + '.http_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; + setElementValue('cbid.shadowsocksr.' + sid + '.http_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + setElementValue('cbid.shadowsocksr.' + sid + '.http_path', params.get("path") ? decodeURIComponent(params.get("path")) : ""); } break; } diff --git a/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po b/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po index 7621a513a..6d547ca19 100644 --- a/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po +++ b/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po @@ -91,6 +91,8 @@ msgstr "TLS 主机名" msgid "allowInsecure" msgstr "允许不安全连接" +msgid "Enabling MPTCP Requires Server Support." +msgstr "启用 MPTCP 需服务端支持。" msgid "concurrency" msgstr "TCP 最大并发连接数" @@ -377,6 +379,12 @@ msgstr "强制走代理" msgid "UDP Relay" msgstr "UDP 中继" +msgid "Restart ShadowSocksR Plus+" +msgstr "重启 ShadowSocksR Plus+" + +msgid "Restart Service" +msgstr "重启服务" + msgid "Google Connectivity" msgstr "【谷歌】连通性检查" @@ -863,6 +871,30 @@ msgstr "本机服务端" msgid "Global SOCKS5 Proxy Server" msgstr "SOCKS5 代理服务端(全局)" +msgid "Socks5 Auth Mode" +msgstr "Socks5 认证方式" + +msgid "Socks protocol auth methods, default:noauth." +msgstr "Socks 协议的认证方式,默认值:noauth。" + +msgid "Socks5 User" +msgstr "Socks5 用户名" + +msgid "Only when auth is password valid, Mandatory." +msgstr "仅当 auth 为 password 时有效,必填。" + +msgid "Socks5 Password" +msgstr "Socks5 密码" + +msgid "Only when auth is password valid, Not mandatory." +msgstr "仅当 auth 为 password 时有效,非必填。" + +msgid "Enabled Mixed" +msgstr "启用 Mixed" + +msgid "Mixed as an alias of socks, default:Enabled." +msgstr "Mixed 作为 SOCKS 的别名,默认:启用。" + msgid "Xray Fragment Settings" msgstr "Xray 分片设置" @@ -1147,3 +1179,45 @@ msgstr "socks5 服务器可以从外部接收的最大数据包大小(单位 msgid "Disable ChinaDNS-NG" msgstr "直通模式(禁用 ChinaDNS-NG)" + +msgid "Clear logs" +msgstr "清空日志" + +msgid "Backup and Restore" +msgstr "备份还原" + +msgid "Backup or Restore Client and Server Configurations." +msgstr "备份或还原客户端及服务端配置。" + +msgid "Note: Restoring configurations across different versions may cause compatibility issues." +msgstr "注意:不同版本间的配置恢复可能会导致兼容性问题。" + +msgid "Create Backup File" +msgstr "创建备份文件" + +msgid "Restore Backup File" +msgstr "恢复备份文件" + +msgid "DL Backup" +msgstr "下载备份" + +msgid "RST Backup" +msgstr "恢复备份" + +msgid "UL Restore" +msgstr "上传恢复" + +msgid "CLOSE WIN" +msgstr "关闭窗口" + +msgid "Restore to default configuration" +msgstr "恢复默认配置" + +msgid "Do Reset" +msgstr "执行重置" + +msgid "Do you want to restore the client to default settings?" +msgstr "是否要恢复客户端默认配置?" + +msgid "Are you sure you want to restore the client to default settings?" +msgstr "是否真的要恢复客户端默认配置?" diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua index 569b46263..8210ac360 100755 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua @@ -12,6 +12,7 @@ local chain = arg[5] or "0" local chain_local_port = string.split(chain, "/")[2] or "0" local server = ucursor:get_all("shadowsocksr", server_section) +local socks_server = ucursor:get_all("shadowsocksr", "@socks5_proxy[0]") or {} local xray_fragment = ucursor:get_all("shadowsocksr", "@global_xray_fragment[0]") or {} local xray_noise = ucursor:get_all("shadowsocksr", "@xray_noise_packets[0]") or {} local outbound_settings = nil @@ -28,7 +29,7 @@ function vmess_vless() alterId = (server.v2ray_protocol == "vmess" or not server.v2ray_protocol) and tonumber(server.alter_id) or nil, security = (server.v2ray_protocol == "vmess" or not server.v2ray_protocol) and server.security or nil, encryption = (server.v2ray_protocol == "vless") and server.vless_encryption or nil, - flow = ((server.xtls == '1') or (server.tls == '1') or (server.reality == '1')) and server.tls_flow or nil + flow = (((server.xtls == '1') or (server.tls == '1') or (server.reality == '1')) and server.tls_flow ~= "none") and server.tls_flow or nil } } } @@ -180,10 +181,20 @@ end -- 检查是否启用 socks 代理 if proto:find("tcp") and socks_port ~= "0" then table.insert(Xray.inbounds, { - -- socks + -- socks protocol = "socks", port = tonumber(socks_port), - settings = {auth = "noauth", udp = true} + settings = { + auth = socks_server.socks5_auth, + udp = true, + mixed = (socks_server.socks5_mixed == '1') and true or false, + accounts = (socks_server.socks5_auth ~= "noauth") and { + { + user = socks_server.socks5_user, + pass = socks_server.socks5_pass + } + } or nil + } }) end @@ -220,26 +231,15 @@ end fingerprint = server.fingerprint, serverName = server.tls_host } or nil, - tcpSettings = (server.transport == "tcp" and server.tcp_guise == "http") and { + rawSettings = (server.transport == "raw" or server.transport == "tcp") and { -- tcp header = { - type = server.tcp_guise, - request = { + type = server.tcp_guise or "none", + request = (server.tcp_guise == "http") and { -- request path = {server.http_path} or {"/"}, headers = {Host = {server.http_host} or {}} - } - } - } or nil, - rawSettings = (server.transport == "raw" and server.raw_guise == "http") and { - -- raw - header = { - type = server.raw_guise, - request = { - -- request - path = {server.http_path} or {"/"}, - headers = {Host = {server.http_host} or {}} - } + } or nil } } or nil, kcpSettings = (server.transport == "kcp") and { @@ -256,10 +256,7 @@ end } or nil, wsSettings = (server.transport == "ws") and (server.ws_path or server.ws_host or server.tls_host) and { -- ws - headers = (server.ws_host or server.tls_host) and { - -- headers - Host = server.ws_host or server.tls_host - } or nil, + Host = server.ws_host or server.tls_host or nil, path = server.ws_path, maxEarlyData = tonumber(server.ws_ed) or nil, earlyDataHeaderName = server.ws_ed_header or nil @@ -297,8 +294,9 @@ end initial_windows_size = tonumber(server.initial_windows_size) or nil } or nil, sockopt = { - tcpMptcp = (server.mptcp == "1") and true or false, -- MPTCP - tcpNoDelay = (server.mptcp == "1") and true or false, -- MPTCP + mark = 250, + tcpMptcp = (server.mptcp == "1") and true or nil, -- MPTCP + tcpNoDelay = (server.mptcp == "1") and true or nil, -- MPTCP tcpcongestion = server.custom_tcpcongestion, -- 连接服务器节点的 TCP 拥塞控制算法 dialerProxy = (xray_fragment.fragment == "1" or xray_fragment.noise == "1") and "dialerproxy" or nil } @@ -335,8 +333,10 @@ if xray_fragment.fragment ~= "0" or (xray_fragment.noise ~= "0" and xray_noise.e }, streamSettings = { sockopt = { - tcpMptcp = (server.mptcp == "1") and true or false, -- MPTCP - tcpNoDelay = (server.mptcp == "1") and true or false -- MPTCP + mark = 250, + tcpMptcp = (server.mptcp == "1") and true or nil, -- MPTCP + tcpNoDelay = (server.mptcp == "1") and true or nil, -- MPTCP + tcpcongestion = server.custom_tcpcongestion -- 连接服务器节点的 TCP 拥塞控制算法 } } }) diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua index ccdd8be7a..777923a18 100755 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua @@ -172,6 +172,9 @@ local function processData(szType, content) result.v2ray_protocol = 'vmess' result.server = info.add result.server_port = info.port + if info.net == "tcp" then + info.net = "raw" + end result.transport = info.net result.alter_id = info.aid result.vmess_id = info.id @@ -194,7 +197,7 @@ local function processData(szType, content) result.h2_host = info.host result.h2_path = info.path end - if info.net == 'tcp' then + if info.net == 'raw' or info.net == 'tcp' then if info.type and info.type ~= "http" then info.type = "none" end @@ -316,6 +319,7 @@ local function processData(szType, content) result.server = nil end elseif szType == "trojan" then + local params = {} local idx_sp = 0 local alias = "" if content:find("#") then @@ -324,20 +328,27 @@ local function processData(szType, content) end local info = content:sub(1, idx_sp - 1) local hostInfo = split(info, "@") - local host = split(hostInfo[2], ":") local userinfo = hostInfo[1] local password = userinfo + + -- 分离服务器地址和端口 + local host = split(hostInfo[2], ":") + local server = host[1] + local port = host[2] + result.alias = UrlDecode(alias) result.type = v2_tj result.v2ray_protocol = "trojan" - result.server = host[1] + result.server = server + result.password = password + -- 按照官方的建议 默认验证ssl证书 result.insecure = "0" result.tls = "1" - if host[2]:find("?") then - local query = split(host[2], "?") + + if port:find("?") then + local query = split(port, "?") result.server_port = query[1] - local params = {} for _, v in pairs(split(query[2], '&')) do local t = split(v, '=') params[t[1]] = t[2] @@ -346,10 +357,62 @@ local function processData(szType, content) -- 未指定peer(sni)默认使用remote addr result.tls_host = params.sni end + + if params.allowInsecure then + -- 处理 insecure 参数 + result.insecure = params.allowInsecure + end else - result.server_port = host[2] + result.server_port = port + end + + if v2_tj ~= "trojan" then + if params.fp then + -- 处理 fingerprint 参数 + result.fingerprint = params.fp + end + -- 处理传输协议 + result.transport = params.type or "tcp" -- 默认传输协议为 tcp + if result.transport == "tcp" then + result.transport = "raw" + 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 == "splithttp" then + result.splithttp_host = (result.tls ~= "1") and (params.host and UrlDecode(params.host)) or nil + result.splithttp_path = params.path and UrlDecode(params.path) or "/" + 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 end - result.password = password elseif szType == "vless" then local url = URL.parse("http://" .. content) local params = url.query @@ -400,18 +463,12 @@ local function processData(szType, content) elseif result.transport == "grpc" then result.serviceName = params.serviceName result.grpc_mode = params.mode or "gun" - elseif result.transport == "tcp" then + 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 - elseif result.transport == "raw" then - result.raw_guise = params.headerType or "none" - if result.raw_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 if not result.alias then diff --git a/patch-luci-app-ssr-plus.patch b/patch-luci-app-ssr-plus.patch index b027ebbb5..7c4d31e89 100644 --- a/patch-luci-app-ssr-plus.patch +++ b/patch-luci-app-ssr-plus.patch @@ -121,7 +121,7 @@ index b469328..3a0b055 100644 bool "Include ShadowsocksR Libev Client" default y diff --git a/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua b/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua -index 8ceaba7..f381a54 100644 +index 0469b87..eb7ccb7 100644 --- a/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua +++ b/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua @@ -7,7 +7,7 @@ function index() @@ -134,10 +134,10 @@ index 8ceaba7..f381a54 100644 page.acl_depends = { "luci-app-ssr-plus" } entry({"admin", "services", "shadowsocksr", "client"}, cbi("shadowsocksr/client"), _("SSR Client"), 10).leaf = true diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua -index 9853997..de97787 100644 +index 88351ff..e5f1593 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua -@@ -70,45 +70,6 @@ o.default = "https://fastly.jsdelivr.net/gh/QiuSimons/Netflix_IP/NF_only.txt" +@@ -75,45 +75,6 @@ o.default = "https://fastly.jsdelivr.net/gh/QiuSimons/Netflix_IP/NF_only.txt" o.description = translate("Customize Netflix IP Url") o:depends("netflix_enable", "1") @@ -238,7 +238,7 @@ index 26de9ba..b24183e 100644 o = s:option(Value, "chinadns_forward", translate("Domestic DNS Server")) o:value("", translate("Disable ChinaDNS-NG")) diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua -index 2a5c5e2..65b916e 100644 +index c7e84ec..83a88a2 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua @@ -92,7 +92,7 @@ if Process_list:find("ssr.server") then @@ -338,7 +338,7 @@ index d0b77f1..259cb7f 100644 res(); }); diff --git a/luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm b/luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm -index 0e19670..a7485ac 100644 +index 2607fb7..62e1b1c 100644 --- a/luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm +++ b/luci-app-ssr-plus/luasrc/view/shadowsocksr/ssrurl.htm @@ -69,9 +69,9 @@ function export_ssr_url(btn, urlname, sid) { @@ -398,25 +398,25 @@ index 0e19670..a7485ac 100644 return false; case "trojan": try { -@@ -234,7 +234,7 @@ function import_ssr_url(btn, urlname, sid) { - document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls')[0].dispatchEvent(event); - document.getElementsByName('cbid.shadowsocksr.' + sid + '.tls_host')[0].value = url.searchParams.get("sni"); +@@ -293,7 +293,7 @@ function import_ssr_url(btn, urlname, sid) { + break; + } - s.innerHTML = "<%:Import configuration information successfully.%>"; + s.innerHTML = "<%:Import configuration information successfully.%>"; return false; case "vmess": var sstr = b64DecodeUnicode(ssu[1]); -@@ -296,7 +296,7 @@ function import_ssr_url(btn, urlname, sid) { - } +@@ -358,7 +358,7 @@ function import_ssr_url(btn, urlname, sid) { document.getElementsByName('cbid.shadowsocksr.' + sid + '.mux')[0].checked = true; document.getElementsByName('cbid.shadowsocksr.' + sid + '.mux')[0].dispatchEvent(event); + } - s.innerHTML = "<%:Import configuration information successfully.%>"; + s.innerHTML = "<%:Import configuration information successfully.%>"; return false; case "vless": try { -@@ -388,10 +388,10 @@ function import_ssr_url(btn, urlname, sid) { +@@ -463,10 +463,10 @@ function import_ssr_url(btn, urlname, sid) { } break; } @@ -430,10 +430,10 @@ index 0e19670..a7485ac 100644 } } diff --git a/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po b/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po -index 69dae19..7621a51 100644 +index 9992e81..6d547ca 100644 --- a/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po +++ b/luci-app-ssr-plus/po/zh_Hans/ssr-plus.po -@@ -569,27 +569,6 @@ msgstr "使用 DNS2TCP 查询" +@@ -577,27 +577,6 @@ msgstr "使用 DNS2TCP 查询" msgid "Use DNS2SOCKS query and cache" msgstr "使用 DNS2SOCKS 查询并缓存"