diff --git a/luasrc/view/admin_network/user_status.htm b/luasrc/view/admin_network/user_status.htm deleted file mode 100755 index 0519ecba6..000000000 --- a/luasrc/view/admin_network/user_status.htm +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/luci-app-bypass/luasrc/model/cbi/bypass/client-config.lua b/luci-app-bypass/luasrc/model/cbi/bypass/client-config.lua index 77e53bd17..b4152edc3 100644 --- a/luci-app-bypass/luasrc/model/cbi/bypass/client-config.lua +++ b/luci-app-bypass/luasrc/model/cbi/bypass/client-config.lua @@ -4,6 +4,7 @@ require "nixio.fs" require "luci.sys" require "luci.http" +require "luci.jsonc" require "luci.model.ipkg" local m, s, o @@ -433,7 +434,7 @@ o:depends("type", "shadowtls") o.default = "1" o.rmempty = false -o = s:option(Flag, "fastopen", translate("TCP Fast Open")) +o = s:option(Flag, "fastopen", translate("TCP Fast Open"), translate("Enabling TCP Fast Open Requires Server Support.")) o:depends("type", "shadowtls") o.default = "0" o.rmempty = false @@ -618,11 +619,12 @@ o:depends({type = "v2ray", v2ray_protocol = "socks"}) -- 传输协议 o = s:option(ListValue, "transport", translate("Transport")) -o:value("tcp", "TCP") +o:value("raw", "RAW (TCP)") o:value("kcp", "mKCP") o:value("ws", "WebSocket") o:value("httpupgrade", "HTTPUpgrade") o:value("splithttp", "SplitHTTP") +o:value("xhttp", "XHTTP") o:value("h2", "HTTP/2") o:value("quic", "QUIC") o:value("grpc", "gRPC") @@ -634,10 +636,10 @@ 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:depends("transport", "raw") o:value("none", translate("None")) o:value("http", "HTTP") o.rmempty = true @@ -703,6 +705,78 @@ o = s:option(Value, "splithttp_path", translate("Splithttp Path")) o:depends("transport", "splithttp") o.rmempty = true +-- [[ XHTTP部分 ]]-- +o = s:option(ListValue, "xhttp_alpn", translate("XHTTP Alpn")) +o.default = "" +o:value("", translate("Default")) +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("transport", "xhttp") + +o = s:option(ListValue, "xhttp_mode", translate("XHTTP Mode")) +o:depends("transport", "xhttp") +o.default = "auto" +o:value("auto") +o:value("packet-up") +o:value("stream-up") +o:value("stream-one") + +o = s:option(Value, "xhttp_host", translate("XHTTP Host")) +o:depends({transport = "xhttp", tls = false}) +o.rmempty = true + +o = s:option(Value, "xhttp_path", translate("XHTTP Path")) +o.placeholder = "/" +o:depends("transport", "xhttp") +o.rmempty = true + +o = s:option(Flag, "enable_xhttp_extra", translate("XHTTP Extra")) +o.description = translate("Enable this option to configure XHTTP Extra (JSON format).") +o.rmempty = true +o.default = "0" +o:depends("transport", "xhttp") + +o = s:option(TextValue, "xhttp_extra", " ") +o.description = translate( + "" .. translate("Configure XHTTP Extra Settings (JSON format), see:") .. "" .. + " " .. + "" .. translate("Click to the page") .. "") +o:depends("enable_xhttp_extra", true) +o.rmempty = true +o.rows = 10 +o.wrap = "off" +o.custom_write = function(self, section, value) + m:set(section, "xhttp_extra", value) + local success, data = pcall(luci.jsonc.parse, value) + if success and data then + local address = (data.extra and data.extra.downloadSettings and data.extra.downloadSettings.address) + or (data.downloadSettings and data.downloadSettings.address) + if address and address ~= "" then + m:set(section, "download_address", address) + else + m:del(section, "download_address") + end + else + m:del(section, "download_address") + end +end +o.validate = function(self, value) + value = value:gsub("\r\n", "\n"):gsub("^[ \t]*\n", ""):gsub("\n[ \t]*$", ""):gsub("\n[ \t]*\n", "\n") + if value:sub(-1) == "\n" then + value = value:sub(1, -2) + end + local success, data = pcall(luci.jsonc.parse, value) + if not success or not data then + return nil, translate("Invalid JSON format") + end + + return value +end + -- [[ H2部分 ]]-- -- H2域名 @@ -924,15 +998,33 @@ 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 = "tcp", reality = true}) + o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "raw", tls = true}) + o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "raw", reality = true}) + + o = s:option(ListValue, "xhttp_tls_flow", translate("Flow")) + for _, v in ipairs(tls_flows) do + if v == "none" then + o.default = "none" + o:value("none", translate("none")) + else + o:value("xtls-rprx-vision", translate("xtls-rprx-vision")) + end + end + o.rmempty = true + o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "xhttp", tls = true}) + o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "xhttp", reality = true}) -- [[ uTLS ]]-- - o = s:option(Value, "fingerprint", translate("Finger Print")) - o.default = "chrome" + o = s:option(ListValue, "fingerprint", translate("Finger Print")) + o.default = "" o:value("chrome", translate("chrome")) o:value("firefox", translate("firefox")) o:value("safari", translate("safari")) @@ -973,19 +1065,37 @@ o:depends({type = "hysteria", insecure = true }) o.rmempty = true --- [[ Mux ]]-- -o = s:option(Flag, "mux", translate("Mux")) +-- [[ Mux.Cool ]] -- +o = s:option(Flag, "mux", translate("Mux"), translate("Enable Mux.Cool")) o.rmempty = false o.default = false -o:depends({type = "v2ray", v2ray_protocol = "vless"}) +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "raw"}) +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "ws"}) +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "kcp"}) +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "httpupgrade"}) +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "splithttp"}) +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "h2"}) +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "quic"}) +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "grpc"}) o:depends({type = "v2ray", v2ray_protocol = "vmess"}) o:depends({type = "v2ray", v2ray_protocol = "trojan"}) o:depends({type = "v2ray", v2ray_protocol = "shadowsocks"}) o:depends({type = "v2ray", v2ray_protocol = "socks"}) o:depends({type = "v2ray", v2ray_protocol = "http"}) +-- [[ XUDP Mux ]] -- +o = s:option(Flag, "xmux", translate("Xudp Mux"), translate("Enable Xudp Mux")) +o.rmempty = false +o.default = false +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "xhttp"}) + -- [[ TCP 最大并发连接数 ]]-- -o = s:option(ListValue, "concurrency", translate("concurrency")) +o = s:option(Value, "concurrency", translate("concurrency")) +o.description = translate( + "") o.rmempty = true o.default = "-1" o:value("-1", translate("disable")) @@ -993,15 +1103,27 @@ o:value("8", translate("8")) o:depends("mux", true) -- [[ UDP 最大并发连接数 ]]-- -o = s:option(ListValue, "xudpConcurrency", translate("xudpConcurrency")) +o = s:option(Value, "xudpConcurrency", translate("xudpConcurrency")) +o.description = translate( + "") o.rmempty = true o.default = "16" o:value("-1", translate("disable")) o:value("16", translate("16")) o:depends("mux", true) +o:depends("xmux", true) -- [[ 对被代理的 UDP/443 流量处理方式 ]]-- o = s:option(ListValue, "xudpProxyUDP443", translate("xudpProxyUDP443")) +o.description = translate( + "") o.rmempty = true o.default = "reject" o:value("reject", translate("reject")) @@ -1009,11 +1131,16 @@ o:value("allow", translate("allow")) o:value("skip", translate("skip")) o:depends("mux", true) +-- [[ XHTTP TCP Fast Open ]]-- +o = s:option(Flag, "tcpfastopen", translate("TCP Fast Open"), translate("Enabling TCP Fast Open Requires Server Support.")) +o.rmempty = true +o.default = "0" +o:depends({type = "v2ray", v2ray_protocol = "vless", transport = "xhttp"}) -- [[ MPTCP ]]-- -o = s:option(Flag, "mptcp", translate("MPTCP")) -o.rmempty = false -o.default = false +o = s:option(Flag, "mptcp", translate("MPTCP"), translate("Enable Multipath TCP, need to be enabled in both server and client configuration.")) +o.rmempty = true +o.default = "0" o:depends({type = "v2ray", v2ray_protocol = "vless"}) o:depends({type = "v2ray", v2ray_protocol = "vmess"}) o:depends({type = "v2ray", v2ray_protocol = "trojan"}) @@ -1088,7 +1215,7 @@ o:value("/etc/ssl/private/ca.pem") o.description = translate("Please confirm the current certificate path") o.default = "/etc/ssl/private/ca.pem" -o = s:option(Flag, "fast_open", translate("TCP Fast Open")) +o = s:option(Flag, "fast_open", translate("TCP Fast Open"), translate("Enabling TCP Fast Open Requires Server Support.")) o.rmempty = true o.default = "0" o:depends("type", "ssr") diff --git a/luci-app-bypass/luasrc/model/cbi/bypass/control.lua b/luci-app-bypass/luasrc/model/cbi/bypass/control.lua index 2ca2e872d..a71446ef9 100644 --- a/luci-app-bypass/luasrc/model/cbi/bypass/control.lua +++ b/luci-app-bypass/luasrc/model/cbi/bypass/control.lua @@ -1,3 +1,4 @@ +require "luci.sys" local fs = require "nixio.fs" local m,s,o @@ -132,4 +133,11 @@ o.validate = function(self, value) return value 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/bypass reload > /dev/null 2>&1 &") + end +end + return m diff --git a/luci-app-bypass/luasrc/view/bypass/server_list.htm b/luci-app-bypass/luasrc/view/bypass/server_list.htm index 466a1058e..c0ee49601 100644 --- a/luci-app-bypass/luasrc/view/bypass/server_list.htm +++ b/luci-app-bypass/luasrc/view/bypass/server_list.htm @@ -14,7 +14,7 @@ local dsp=require "luci.dispatcher" return new Promise((res) =>{ const dom=doms[index]; const port=ports[index]; - if (!dom) res() + if (!dom) res(); port.innerHTML='<%:connecting%>'; XHR.get('<%=dsp.build_url("admin/services/bypass/ping")%>',{ index, @@ -28,7 +28,7 @@ local dsp=require "luci.dispatcher" if (result.ping < 200) col='#ff7700'; if (result.ping < 100) col='#249400'; } - dom.innerHTML=`${(result.ping ? result.ping : "--") + " ms"}` + dom.innerHTML=`${(result.ping ? result.ping : "--") + " ms"}`; if (result.socket){ port.innerHTML='<%:ok%>'; } else{ @@ -36,20 +36,20 @@ local dsp=require "luci.dispatcher" } res(); }); - }) - } + }); + }; let task=-1; const thread=() =>{ - task=task + 1 + task=task + 1; if (doms[task]){ xhr(task).then(thread); } - } + }; for (let i=0; i < 20; i++){ - thread() + thread(); } - }) + }); function cbi_row_drop(fromId,toId,store,isToBottom){ var fromNode=document.getElementById(fromId); @@ -181,4 +181,5 @@ local dsp=require "luci.dispatcher" console.error(err); } } + //]]> diff --git a/luci-app-bypass/luasrc/view/bypass/ssrurl.htm b/luci-app-bypass/luasrc/view/bypass/ssrurl.htm index 5867db34a..3db681b1b 100644 --- a/luci-app-bypass/luasrc/view/bypass/ssrurl.htm +++ b/luci-app-bypass/luasrc/view/bypass/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,77 @@ function import_ssr_url(btn, urlname, sid) { document.getElementsByName('cbid.bypass.' + sid + '.password')[0].value = decodeURIComponent(url.username); document.getElementsByName('cbid.bypass.' + sid + '.tls')[0].checked = true; document.getElementsByName('cbid.bypass.' + sid + '.tls')[0].dispatchEvent(event); - document.getElementsByName('cbid.bypass.' + sid + '.tls_host')[0].value = url.searchParams.get("sni"); + document.getElementsByName('cbid.bypass.' + sid + '.fingerprint')[0].value = params.get("fp") || ""; + document.getElementsByName('cbid.bypass.' + sid + '.tls_host')[0].value = params.get("sni"); + if (params.get("allowInsecure") === "1") { + document.getElementsByName('cbid.bypass.' + sid + '.insecure')[0].checked = true; // 设置 insecure 为 true + document.getElementsByName('cbid.bypass.' + sid + '.insecure')[0].dispatchEvent(event); // 触发事件 + } + document.getElementsByName('cbid.bypass.' + sid + '.transport')[0].value = + params.get("type") == "http" ? "h2" : + (["tcp", "raw"].includes(params.get("type")) ? "raw" : + (params.get("type") || "raw")); + document.getElementsByName('cbid.bypass.' + sid + '.transport')[0].dispatchEvent(event); + switch (params.get("type")) { + case "ws": + if (params.get("security") !== "tls") { + setElementValue('cbid.bypass.' + sid + '.ws_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.bypass.' + sid + '.ws_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); + break; + case "httpupgrade": + if (params.get("security") !== "tls") { + document.getElementsByName('cbid.bypass.' + sid + '.httpupgrade_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + } + document.getElementsByName('cbid.bypass.' + sid + '.httpupgrade_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + break; + case "splithttp": + if (params.get("security") !== "tls") { + document.getElementsByName('cbid.bypass.' + sid + '.splithttp_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + } + document.getElementsByName('cbid.bypass.' + sid + '.splithttp_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + break; + case "xhttp": + if (params.get("security") !== "tls") { + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + } + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_mode')[0].value = params.get("mode") || "auto"; + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + if (params.get("extra") && params.get("extra").trim() !== "") { + document.getElementsByName('cbid.bypass.' + sid + '.enable_xhttp_extra')[0].checked = true; // 设置 enable_xhttp_extra 为 true + document.getElementsByName('cbid.bypass.' + sid + '.enable_xhttp_extra')[0].dispatchEvent(event); // 触发事件 + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_extra')[0].value = params.get("extra") || ""; + } + break; + case "kcp": + document.getElementsByName('cbid.bypass.' + sid + '.kcp_guise')[0].value = params.get("headerType") || "none"; + document.getElementsByName('cbid.bypass.' + sid + '.seed')[0].value = params.get("seed") || ""; + break; + case "http": + /* this is non-standard, bullshit */ + case "h2": + document.getElementsByName('cbid.bypass.' + sid + '.h2_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + document.getElementsByName('cbid.bypass.' + sid + '.h2_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; + break; + case "quic": + document.getElementsByName('cbid.bypass.' + sid + '.quic_guise')[0].value = params.get("headerType") || "none"; + document.getElementsByName('cbid.bypass.' + sid + '.quic_security')[0].value = params.get("quicSecurity") || "none"; + document.getElementsByName('cbid.bypass.' + sid + '.quic_key')[0].value = params.get("key") || ""; + break; + case "grpc": + document.getElementsByName('cbid.bypass.' + sid + '.serviceName')[0].value = params.get("serviceName") || ""; + document.getElementsByName('cbid.bypass.' + sid + '.grpc_mode')[0].value = params.get("mode") || "gun"; + break; + case "raw": + case "tcp": + document.getElementsByName('cbid.bypass.' + sid + '.tcp_guise')[0].value = params.get("headerType") || "none"; + document.getElementsByName('cbid.bypass.' + sid + '.tcp_guise')[0].dispatchEvent(event); + if (params.get("headerType") === "http") { + document.getElementsByName('cbid.bypass.' + sid + '.http_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; + document.getElementsByName('cbid.bypass.' + sid + '.http_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; + } + break; + } s.innerHTML = "<%:Import configuration information successfully.%>"; return false; @@ -254,21 +325,41 @@ function import_ssr_url(btn, urlname, sid) { document.getElementsByName('cbid.bypass.' + sid + '.server_port')[0].value = ssm.port; document.getElementsByName('cbid.bypass.' + sid + '.alter_id')[0].value = ssm.aid; document.getElementsByName('cbid.bypass.' + sid + '.vmess_id')[0].value = ssm.id; - document.getElementsByName('cbid.bypass.' + sid + '.transport')[0].value = ssm.net; + document.getElementsByName('cbid.bypass.' + sid + '.transport')[0].value = + (ssm.net === "raw" || ssm.net === "tcp") ? "raw" : ssm.net; document.getElementsByName('cbid.bypass.' + 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.bypass.' + sid + '.http_host')[0].value = ssm.host; + document.getElementsByName('cbid.bypass.' + sid + '.http_path')[0].value = ssm.path; } document.getElementsByName('cbid.bypass.' + sid + '.tcp_guise')[0].value = ssm.type; document.getElementsByName('cbid.bypass.' + sid + '.tcp_guise')[0].dispatchEvent(event); - document.getElementsByName('cbid.bypass.' + sid + '.http_host')[0].value = ssm.host; - document.getElementsByName('cbid.bypass.' + sid + '.http_path')[0].value = ssm.path; } if (ssm.net == "ws") { document.getElementsByName('cbid.bypass.' + sid + '.ws_host')[0].value = ssm.host; document.getElementsByName('cbid.bypass.' + sid + '.ws_path')[0].value = ssm.path; } + if (ssm.net == "httpupgrade") { + document.getElementsByName('cbid.bypass.' + sid + '.httpupgrade_host')[0].value = ssm.host; + document.getElementsByName('cbid.bypass.' + sid + '.httpupgrade_path')[0].value = ssm.path; + } + if (ssm.net == "splithttp") { + document.getElementsByName('cbid.bypass.' + sid + '.splithttp_host')[0].value = ssm.host; + document.getElementsByName('cbid.bypass.' + sid + '.splithttp_path')[0].value = ssm.path; + } + if (ssm.net == "xhttp") { + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_mode')[0].value = ssm.mode; + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_host')[0].value = ssm.host; + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_path')[0].value = ssm.path; + if (params.get("extra") && params.get("extra").trim() !== "") { + document.getElementsByName('cbid.bypass.' + sid + '.enable_xhttp_extra')[0].checked = true; // 设置 enable_xhttp_extra 为 true + document.getElementsByName('cbid.bypass.' + sid + '.enable_xhttp_extra')[0].dispatchEvent(event); // 触发事件 + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_extra')[0].value = ssm.extra; + } + } if (ssm.net == "h2") { document.getElementsByName('cbid.bypass.' + sid + '.h2_host')[0].value = ssm.host; document.getElementsByName('cbid.bypass.' + sid + '.h2_path')[0].value = ssm.path; @@ -283,10 +374,20 @@ function import_ssr_url(btn, urlname, sid) { if (ssm.tls == "tls") { document.getElementsByName('cbid.bypass.' + sid + '.tls')[0].checked = true; document.getElementsByName('cbid.bypass.' + sid + '.tls')[0].dispatchEvent(event); + document.getElementsByName('cbid.bypass.' + sid + '.fingerprint')[0].value = ssm.fp; + if (ssm.net == "xhttp") { + document.getElementsByName('cbid.bypass.' + sid + '.xhttp_alpn')[0].value = ssm.alpn; + } document.getElementsByName('cbid.bypass.' + sid + '.tls_host')[0].value = ssm.sni || ssm.host; } - document.getElementsByName('cbid.bypass.' + sid + '.mux')[0].checked = true; - document.getElementsByName('cbid.bypass.' + sid + '.mux')[0].dispatchEvent(event); + if (ssm.mux !== undefined) { + document.getElementsByName('cbid.bypass.' + sid + '.mux')[0].checked = true; + document.getElementsByName('cbid.bypass.' + sid + '.mux')[0].dispatchEvent(event); + } + if (ssm.xmux !== undefined) { + document.getElementsByName('cbid.bypass.' + sid + '.xmux')[0].checked = true; + document.getElementsByName('cbid.bypass.' + sid + '.xmux')[0].dispatchEvent(event); + } s.innerHTML = "<%:Import configuration information successfully.%>"; return false; case "vless": @@ -294,66 +395,114 @@ 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.bypass.' + sid + '.alias')[0].value = url.hash ? decodeURIComponent(url.hash.slice(1)) : ""; - document.getElementsByName('cbid.bypass.' + sid + '.type')[0].value = "v2ray"; - document.getElementsByName('cbid.bypass.' + sid + '.type')[0].dispatchEvent(event); - document.getElementsByName('cbid.bypass.' + sid + '.v2ray_protocol')[0].value = "vless"; - document.getElementsByName('cbid.bypass.' + sid + '.v2ray_protocol')[0].dispatchEvent(event); - document.getElementsByName('cbid.bypass.' + sid + '.server')[0].value = url.hostname; - document.getElementsByName('cbid.bypass.' + sid + '.server_port')[0].value = url.port || "80"; - document.getElementsByName('cbid.bypass.' + sid + '.vmess_id')[0].value = url.username; - document.getElementsByName('cbid.bypass.' + sid + '.transport')[0].value = params.get("type") == "http" ? "h2" : params.get("type") || "tcp"; - document.getElementsByName('cbid.bypass.' + sid + '.transport')[0].dispatchEvent(event); - document.getElementsByName('cbid.bypass.' + sid + '.vless_encryption')[0].value = params.get("encryption") || "none"; - if ([ "tls", "reality" ].includes(params.get("security"))) { - document.getElementsByName('cbid.bypass.' + sid + '.' + params.get("security"))[0].checked = true; - document.getElementsByName('cbid.bypass.' + sid + '.' + params.get("security"))[0].dispatchEvent(event); - - document.getElementsByName('cbid.bypass.' + sid + '.fingerprint')[0].value = params.get("fp") || ""; - document.getElementsByName('cbid.bypass.' + sid + '.tls_flow')[0].value = params.get("flow") || ""; - document.getElementsByName('cbid.bypass.' + sid + '.tls_host')[0].value = params.get("sni") || ""; + // 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.bypass.' + sid + '.alias', url.hash ? decodeURIComponent(url.hash.slice(1)) : ""); + setElementValue('cbid.bypass.' + sid + '.type', "v2ray"); + dispatchEventIfExists('cbid.bypass.' + sid + '.type', event); + setElementValue('cbid.bypass.' + sid + '.v2ray_protocol', "vless"); + dispatchEventIfExists('cbid.bypass.' + sid + '.v2ray_protocol', event); + setElementValue('cbid.bypass.' + sid + '.server', url.hostname); + setElementValue('cbid.bypass.' + sid + '.server_port', url.port || "80"); + setElementValue('cbid.bypass.' + sid + '.vmess_id', url.username); + setElementValue('cbid.bypass.' + sid + '.transport', + params.get("type") === "http" ? "h2" : + (["tcp", "raw"].includes(params.get("type")) ? "raw" : + (params.get("type") || "tcp")) + ); + dispatchEventIfExists('cbid.bypass.' + sid + '.transport', event); + setElementValue('cbid.bypass.' + sid + '.vless_encryption', params.get("encryption") || "none"); + if ([ "tls", "xtls", "reality" ].includes(params.get("security"))) { + setElementValue('cbid.bypass.' + sid + '.' + params.get("security"), true); + dispatchEventIfExists('cbid.bypass.' + sid + '.' + params.get("security"), event); if (params.get("security") === "reality") { - document.getElementsByName('cbid.bypass.' + sid + '.reality_publickey')[0].value = params.get("pbk") ? decodeURIComponent(params.get("pbk")) : ""; - document.getElementsByName('cbid.bypass.' + sid + '.reality_shortid')[0].value = params.get("sid") || ""; - document.getElementsByName('cbid.bypass.' + sid + '.reality_spiderx')[0].value = params.get("spx") ? decodeURIComponent(params.get("spx")) : ""; + setElementValue('cbid.bypass.' + sid + '.reality_publickey', params.get("pbk") ? decodeURIComponent(params.get("pbk")) : ""); + setElementValue('cbid.bypass.' + sid + '.reality_shortid', params.get("sid") || ""); + setElementValue('cbid.bypass.' + sid + '.reality_spiderx', params.get("spx") ? decodeURIComponent(params.get("spx")) : ""); } + setElementValue('cbid.bypass.' + sid + '.tls_flow', params.get("flow") || "none"); + dispatchEventIfExists('cbid.bypass.' + sid + '.tls_flow', event); + + setElementValue('cbid.bypass.' + sid + '.xhttp_alpn', params.get("alpn") || ""); + setElementValue('cbid.bypass.' + sid + '.fingerprint', params.get("fp") || ""); + setElementValue('cbid.bypass.' + sid + '.tls_host', params.get("sni") || ""); } switch (params.get("type")) { case "ws": - if (params.get("security") !== "tls") - document.getElementsByName('cbid.bypass.' + sid + '.ws_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.bypass.' + sid + '.ws_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : "/"; + if (params.get("security") !== "tls") { + setElementValue('cbid.bypass.' + sid + '.ws_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.bypass.' + sid + '.ws_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); + break; + case "httpupgrade": + if (params.get("security") !== "tls") { + setElementValue('cbid.bypass.' + sid + '.httpupgrade_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.bypass.' + sid + '.httpupgrade_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); + break; + case "splithttp": + if (params.get("security") !== "tls") { + setElementValue('cbid.bypass.' + sid + '.splithttp_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.bypass.' + sid + '.splithttp_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); + break; + case "xhttp": + if (params.get("security") !== "tls") { + setElementValue('cbid.bypass.' + sid + '.xhttp_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + } + setElementValue('cbid.bypass.' + sid + '.xhttp_mode', params.get("mode") || "auto"); + setElementValue('cbid.bypass.' + sid + '.xhttp_path', params.get("path") ? decodeURIComponent(params.get("path")) : "/"); + if (params.get("extra") && params.get("extra").trim() !== "") { + setElementValue('cbid.bypass.' + sid + '.enable_xhttp_extra', true); // 设置 enable_xhttp_extra 为 true + dispatchEventIfExists('cbid.bypass.' + sid + '.enable_xhttp_extra', event); // 触发事件 + setElementValue('cbid.bypass.' + sid + '.xhttp_extra', params.get("extra") || ""); + } break; case "kcp": - document.getElementsByName('cbid.bypass.' + sid + '.kcp_guise')[0].value = params.get("headerType") || "none"; - document.getElementsByName('cbid.bypass.' + sid + '.seed')[0].value = params.get("seed") || ""; + setElementValue('cbid.bypass.' + sid + '.kcp_guise', params.get("headerType") || "none"); + setElementValue('cbid.bypass.' + sid + '.seed', params.get("seed") || ""); break; case "http": /* this is non-standard, bullshit */ case "h2": - document.getElementsByName('cbid.bypass.' + sid + '.h2_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.bypass.' + sid + '.h2_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; + setElementValue('cbid.bypass.' + sid + '.h2_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + setElementValue('cbid.bypass.' + sid + '.h2_path', params.get("path") ? decodeURIComponent(params.get("path")) : ""); break; case "quic": - document.getElementsByName('cbid.bypass.' + sid + '.quic_guise')[0].value = params.get("headerType") || "none"; - document.getElementsByName('cbid.bypass.' + sid + '.quic_security')[0].value = params.get("quicSecurity") || "none"; - document.getElementsByName('cbid.bypass.' + sid + '.quic_key')[0].value = params.get("key") || ""; + setElementValue('cbid.bypass.' + sid + '.quic_guise', params.get("headerType") || "none"); + setElementValue('cbid.bypass.' + sid + '.quic_security', params.get("quicSecurity") || "none"); + setElementValue('cbid.bypass.' + sid + '.quic_key', params.get("key") || ""); break; case "grpc": - document.getElementsByName('cbid.bypass.' + sid + '.serviceName')[0].value = params.get("serviceName") || ""; - document.getElementsByName('cbid.bypass.' + sid + '.grpc_mode')[0].value = params.get("mode") || "gun"; + setElementValue('cbid.bypass.' + sid + '.serviceName', params.get("serviceName") || ""); + setElementValue('cbid.bypass.' + sid + '.grpc_mode', params.get("mode") || "gun"); break; case "tcp": - document.getElementsByName('cbid.bypass.' + sid + '.tcp_guise')[0].value = params.get("headerType") || "none"; - document.getElementsByName('cbid.bypass.' + sid + '.tcp_guise')[0].dispatchEvent(event); + case "raw": + setElementValue('cbid.bypass.' + sid + '.tcp_guise', params.get("headerType") || "none"); + dispatchEventIfExists('cbid.bypass.' + sid + '.tcp_guise', event); if (params.get("headerType") === "http") { - document.getElementsByName('cbid.bypass.' + sid + '.http_host')[0].value = params.get("host") ? decodeURIComponent(params.get("host")) : ""; - document.getElementsByName('cbid.bypass.' + sid + '.http_path')[0].value = params.get("path") ? decodeURIComponent(params.get("path")) : ""; + setElementValue('cbid.bypass.' + sid + '.http_host', params.get("host") ? decodeURIComponent(params.get("host")) : ""); + setElementValue('cbid.bypass.' + sid + '.http_path', params.get("path") ? decodeURIComponent(params.get("path")) : ""); } break; } diff --git a/luci-app-bypass/root/etc/init.d/bypass b/luci-app-bypass/root/etc/init.d/bypass index f699b8d95..fc731c00f 100755 --- a/luci-app-bypass/root/etc/init.d/bypass +++ b/luci-app-bypass/root/etc/init.d/bypass @@ -16,12 +16,16 @@ PID=/var/run/smartdns.pid LOG=/var/log/bypass.log BIN_DIR=/usr/share/bypass # Get the default DNSMasq config ID from the UCI configuration -DEFAULT_DNSMASQ_CFGID=$(uci show dhcp.@dnsmasq[0] | awk -F '.' '{print $2}' | awk -F '=' '{print $1}' | head -1) +DEFAULT_DNSMASQ_CFGID="$(uci -q show "dhcp.@dnsmasq[0]" | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')" # Locate the dnsmasq.conf file that contains the conf-dir option DNSMASQ_CONF_PATH=$(grep -l "^conf-dir=" "/tmp/etc/dnsmasq.conf.${DEFAULT_DNSMASQ_CFGID}") # Extract the directory path from the conf-dir line -DNSMASQ_CONF_DIR=$(grep '^conf-dir=' "$DNSMASQ_CONF_PATH" | cut -d'=' -f2 | head -n 1) -DNS_FILE=${DNSMASQ_CONF_DIR%*/}/dnsmasq-by.conf +if [ -f "/tmp/etc/dnsmasq.conf.$DEFAULT_DNSMASQ_CFGID" ]; then + DNSMASQ_CONF_DIR="$(awk -F '=' '/^conf-dir=/ {print $2}' "/tmp/etc/dnsmasq.conf.$DEFAULT_DNSMASQ_CFGID")" +else + DNSMASQ_CONF_DIR="/tmp/dnsmasq.d" +fi +DNS_FILE="${DNSMASQ_CONF_DIR%*/}/dnsmasq-by.conf" DNS_DIR=/tmp/dnsmasq.by O=$DNS_DIR/tmp CRON="grep -q $BIN_DIR $CRON_FILE && sed -i '/\/share\/bypass/d' $CRON_FILE" diff --git a/luci-app-bypass/root/usr/share/bypass/gen_config b/luci-app-bypass/root/usr/share/bypass/gen_config index 98bacef5f..5696bc992 100644 --- a/luci-app-bypass/root/usr/share/bypass/gen_config +++ b/luci-app-bypass/root/usr/share/bypass/gen_config @@ -13,9 +13,12 @@ local chain = arg[6] or "0" local chain_local_port = string.split(chain, "/")[2] or "0" local server = ucursor:get_all("bypass", server_section) +local socks_server = ucursor:get_all("bypass", "@socks5_proxy[0]") or {} +local xray_fragment = ucursor:get_all("bypass", "@global_xray_fragment[0]") or {} +local xray_noise = ucursor:get_all("bypass", "@xray_noise_packets[0]") or {} local outbound_settings = nil -local tls_host = (server.tls_host) and server.tls_host or server.server +local tls_host = (tls_host) and tls_host or server.server function vmess_vless() outbound_settings = { @@ -29,7 +32,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 ((server.xhttp_tls_flow ~= "none") and server.xhttp_tls_flow))) or nil } } } @@ -131,6 +134,8 @@ local Xray = { -- 初始化 inbounds 表 inbounds = {}, + -- 初始化 outbounds 表 + outbounds = {}, } -- 传入连接 -- 添加 dokodemo-door 配置,如果 local_port 不为 0 @@ -177,12 +182,22 @@ end -- 开启 socks 代理 -- 检查是否启用 socks 代理 -if proto:find("tcp") and socks_port ~= "0" then +if proto and 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 = (socks_ip_addr ~= "same") and { + 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 + } or nil }) end @@ -197,7 +212,7 @@ end security = (server.xtls == '1') and "xtls" or (server.tls == '1') and "tls" or (server.reality == '1') and "reality" or nil, tlsSettings = (server.tls == '1') and { -- tls - alpn = server.tls_alpn, + alpn = (server.transport == "xhttp" and server.xhttp_alpn ~= "") and server.xhttp_alpn or server.tls_alpn, fingerprint = server.fingerprint, allowInsecure = (server.insecure == "1"), serverName = tls_host, @@ -213,21 +228,22 @@ end minVersion = "1.3" } or nil, realitySettings = (server.reality == '1') and { + alpn = (server.transport == "xhttp" and server.xhttp_alpn ~= "") and server.xhttp_alpn or nil, publicKey = server.reality_publickey, shortId = server.reality_shortid, spiderX = server.reality_spiderx, fingerprint = server.fingerprint, serverName = 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 } } or nil, kcpSettings = (server.transport == "kcp") and { @@ -244,10 +260,10 @@ end } or nil, wsSettings = (server.transport == "ws") and (server.ws_path or server.ws_host or tls_host) and { -- ws - headers = (server.ws_host or tls_host) and { - -- headers - Host = server.ws_host or tls_host - } or nil, + Host = server.ws_host or tls_host or nil, + + + path = server.ws_path, maxEarlyData = tonumber(server.ws_ed) or nil, earlyDataHeaderName = server.ws_ed_header or nil @@ -262,6 +278,20 @@ end host = (server.splithttp_host or tls_host) or nil, path = server.splithttp_path or "/" } or nil, + xhttpSettings = (server.transport == "xhttp") and { + -- xhttp + mode = server.xhttp_mode or "auto", + host = (server.xhttp_host or tls_host) or nil, + path = server.xhttp_path or "/", + extra = (server.enable_xhttp_extra == "1" and server.xhttp_extra) and (function() + local success, parsed = pcall(json.parse, server.xhttp_extra) + if success then + return parsed.extra or parsed + else + return nil + end + end)() or nil + } or nil, httpSettings = (server.transport == "h2") and { -- h2 path = server.h2_path or "", @@ -285,21 +315,56 @@ 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 - tcpcongestion = server.custom_tcpcongestion -- 连接服务器节点的 TCP 拥塞控制算法 + mark = 250, + tcpFastOpen = ((server.transport == "xhttp" and server.tcpfastopen == "1") and true or false) or (server.transport ~= "xhttp") and nil, -- XHTTP Tcp Fast Open + tcpMptcp = (server.mptcp == "1") and true or nil, -- MPTCP + Penetrate = (server.mptcp == "1") and true or nil, -- Penetrate MPTCP + tcpcongestion = server.custom_tcpcongestion, -- 连接服务器节点的 TCP 拥塞控制算法 + dialerProxy = (xray_fragment.fragment == "1" or xray_fragment.noise == "1") and "dialerproxy" or nil } } or nil, mux = (server.v2ray_protocol ~= "wireguard") and { -- mux - enabled = (server.mux == "1") and true or false, -- Mux - concurrency = tonumber(server.concurrency), -- TCP 最大并发连接数 - xudpConcurrency = tonumber(server.xudpConcurrency), -- UDP 最大并发连接数 - xudpProxyUDP443 = server.xudpProxyUDP443 -- 对被代理的 UDP/443 流量处理方式 + enabled = (server.mux == "1" or server.xmux == "1") and true or false, -- Mux + concurrency = (server.mux == "1" and ((server.concurrency ~= "0") and tonumber(server.concurrency) or 8)) or (server.xmux == "1" and -1) or nil, -- TCP 最大并发连接数 + xudpConcurrency = ((server.xudpConcurrency ~= "0") and tonumber(server.xudpConcurrency)) or nil, -- UDP 最大并发连接数 + xudpProxyUDP443 = (server.mux == "1") and server.xudpProxyUDP443 or nil -- 对被代理的 UDP/443 流量处理方式 } or nil } } +-- 添加带有 fragment 设置的 dialerproxy 配置 +if xray_fragment.fragment ~= "0" or (xray_fragment.noise ~= "0" and xray_noise.enabled ~= "0") then + table.insert(Xray.outbounds, { + protocol = "freedom", + tag = "dialerproxy", + settings = { + domainStrategy = (xray_fragment.noise == "1" and xray_noise.enabled == "1") and xray_noise.domainStrategy, + fragment = (xray_fragment.fragment == "1") and { + packets = (xray_fragment.fragment_packets ~= "") and xray_fragment.fragment_packets or nil, + length = (xray_fragment.fragment_length ~= "") and xray_fragment.fragment_length or nil, + interval = (xray_fragment.fragment_interval ~= "") and xray_fragment.fragment_interval or nil + } or nil, + noises = (xray_fragment.noise == "1" and xray_noise.enabled == "1") and { + { + type = xray_noise.type, + packet = xray_noise.packet, + delay = xray_noise.delay:find("-") and xray_noise.delay or tonumber(xray_noise.delay) + } + } or nil + }, + streamSettings = { + sockopt = { + mark = 250, + tcpFastOpen = ((server.transport == "xhttp" and server.tcpfastopen == "1") and true or false) or (server.transport ~= "xhttp") and nil, -- XHTTP Tcp Fast Open + tcpMptcp = (server.mptcp == "1") and true or nil, -- MPTCP + Penetrate = (server.mptcp == "1") and true or nil, -- Penetrate MPTCP + tcpcongestion = server.custom_tcpcongestion -- 连接服务器节点的 TCP 拥塞控制算法 + } + } + }) +end + local cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA" local cipher13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384" local trojan = { @@ -345,7 +410,7 @@ local ss = { server_port = tonumber(server.server_port), local_address = "0.0.0.0", local_port = tonumber(local_port), - mode = (proto == "tcp,udp") and "tcp_and_udp" or proto .. "_only", + mode = (proto == "tcp,udp") and "tcp_and_udp" or (proto .. "_only"), password = server.password, method = server.encrypt_method_ss, timeout = tonumber(server.timeout), @@ -353,7 +418,7 @@ local ss = { reuse_port = true } local hysteria = { - server = (server.server_port and (server.port_range and (ip_addr .. ":" .. server.server_port .. "," .. server.port_range) or ip_addr .. ":" .. server.server_port) or (server.port_range and ip_addr .. ":" .. server.port_range or ip_addr .. ":443")), + server = (server.server_port and (server.port_range and (ip_addr .. ":" .. server.server_port .. "," .. server.port_range) or (ip_addr .. ":" .. server.server_port) or (server.port_range and ip_addr .. ":" .. server.port_range or ip_addr .. ":443"))), bandwidth = (server.uplink_capacity or server.downlink_capacity) and { up = tonumber(server.uplink_capacity) and tonumber(server.uplink_capacity) .. " mbps" or nil, down = tonumber(server.downlink_capacity) and tonumber(server.downlink_capacity) .. " mbps" or nil diff --git a/luci-app-bypass/root/usr/share/bypass/subscribe b/luci-app-bypass/root/usr/share/bypass/subscribe index 596f26321..f271de45c 100755 --- a/luci-app-bypass/root/usr/share/bypass/subscribe +++ b/luci-app-bypass/root/usr/share/bypass/subscribe @@ -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 @@ -190,11 +193,29 @@ local function processData(szType, content) result.splithttp_host = info.host result.splithttp_path = info.path end + if info.net == 'xhttp' then + result.xhttp_mode = info.mode + result.xhttp_host = info.host + result.xhttp_path = info.path + -- 检查 extra 参数是否存在且非空 + result.enable_xhttp_extra = (info.extra and info.extra ~= "") and "1" or nil + result.xhttp_extra = (info.extra and info.extra ~= "") and info.extra or nil + -- 尝试解析 JSON 数据 + local success, Data = pcall(jsonParse, info.extra) + if success and Data 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 + end if info.net == 'h2' then 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 @@ -228,6 +249,9 @@ local function processData(szType, content) end if info.tls == "tls" or info.tls == "1" then result.tls = "1" + if info.alpn and info.alpn ~= "" then + result.xhttp_alpn = info.alpn + end if info.sni and info.sni ~= "" then result.tls_host = info.sni elseif info.host then @@ -316,6 +340,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,32 +349,113 @@ 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] end + if params.alpn then + -- 处理 alpn 参数 + result.xhttp_alpn = params.alpn + end + if params.sni then -- 未指定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 == "xhttp" 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) + if success and Data 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 end - result.password = password elseif szType == "vless" then local url = URL.parse("http://" .. content) local params = url.query @@ -363,6 +469,7 @@ local function processData(szType, content) result.vless_encryption = params.encryption or "none" result.transport = params.type or "tcp" result.tls = (params.security == "tls" or params.security == "xtls") and "1" or "0" + result.xhttp_alpn = params.alpn or "" result.tls_host = params.sni result.tls_flow = (params.security == "tls" or params.security == "reality") and params.flow or nil result.fingerprint = params.fp @@ -379,6 +486,23 @@ local function processData(szType, content) 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 == "xhttp" 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) + if success and Data 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" @@ -400,7 +524,7 @@ 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 diff --git a/luci-app-bypass/root/usr/share/rpcd/acl.d/luci-app-bypass.json b/luci-app-bypass/root/usr/share/rpcd/acl.d/luci-app-bypass.json index 1840faa5f..59e19cf96 100644 --- a/luci-app-bypass/root/usr/share/rpcd/acl.d/luci-app-bypass.json +++ b/luci-app-bypass/root/usr/share/rpcd/acl.d/luci-app-bypass.json @@ -2,9 +2,15 @@ "luci-app-bypass": { "description": "Grant UCI access for luci-app-bypass", "read": { + "file": { + "/etc/bypass/*": [ "read" ] + }, "uci": [ "bypass" ] }, "write": { + "file": { + "/etc/bypass/*": [ "write" ] + }, "uci": [ "bypass" ] } } diff --git a/luci-app-nekobox/htdocs/nekobox/ping.php b/luci-app-nekobox/htdocs/nekobox/ping.php index bb493b351..a0e628b33 100644 --- a/luci-app-nekobox/htdocs/nekobox/ping.php +++ b/luci-app-nekobox/htdocs/nekobox/ping.php @@ -4026,6 +4026,22 @@ input[type="range"]:focus { } } +@media (max-width: 768px) { + .modal-body .d-flex { + align-items: stretch; + gap: 10px; + } + .modal-body .btn { + width: 100%; + text-align: center; + margin-bottom: 8px; + padding: 10px; + } + .modal-body .btn:last-child { + margin-bottom: 0; + } +} +