diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index cade7cd17..579aacaf5 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 PKG_VERSION:=24.11.17 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua index 9765793d6..e41f576f3 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua @@ -147,6 +147,7 @@ if has_xray then o = s_xray: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-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua index 32f72bde4..63ab75fce 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua @@ -334,9 +334,10 @@ o:depends({ [option_name("protocol")] = "shadowsocks" }) o = s:option(Flag, option_name("reality"), translate("REALITY")) o.default = 0 -o:depends({ [option_name("tls")] = true, [option_name("transport")] = "tcp" }) +o:depends({ [option_name("tls")] = true, [option_name("transport")] = "raw" }) o:depends({ [option_name("tls")] = true, [option_name("transport")] = "h2" }) o:depends({ [option_name("tls")] = true, [option_name("transport")] = "grpc" }) +o:depends({ [option_name("tls")] = true, [option_name("transport")] = "xhttp" }) o = s:option(ListValue, option_name("alpn"), translate("alpn")) o.default = "default" @@ -392,7 +393,7 @@ o:depends({ [option_name("tls")] = true, [option_name("utls")] = true }) o:depends({ [option_name("tls")] = true, [option_name("reality")] = true }) o = s:option(ListValue, option_name("transport"), translate("Transport")) -o:value("tcp", "TCP") +o:value("raw", "RAW") o:value("mkcp", "mKCP") o:value("ws", "WebSocket") o:value("h2", "HTTP/2") @@ -400,7 +401,7 @@ o:value("ds", "DomainSocket") o:value("quic", "QUIC") o:value("grpc", "gRPC") o:value("httpupgrade", "HttpUpgrade") -o:value("splithttp", "SplitHTTP") +o:value("xhttp", "XHTTP") o:depends({ [option_name("protocol")] = "vmess" }) o:depends({ [option_name("protocol")] = "vless" }) o:depends({ [option_name("protocol")] = "socks" }) @@ -432,13 +433,13 @@ o = s:option(Value, option_name("wireguard_keepAlive"), translate("Keep Alive")) o.default = "0" o:depends({ [option_name("protocol")] = "wireguard" }) --- [[ TCP部分 ]]-- +-- [[ RAW部分 ]]-- -- TCP伪装 o = s:option(ListValue, option_name("tcp_guise"), translate("Camouflage Type")) o:value("none", "none") o:value("http", "http") -o:depends({ [option_name("transport")] = "tcp" }) +o:depends({ [option_name("transport")] = "raw" }) -- HTTP域名 o = s:option(DynamicList, option_name("tcp_guise_http_host"), translate("HTTP Host")) @@ -566,16 +567,115 @@ o = s:option(Value, option_name("httpupgrade_path"), translate("HttpUpgrade Path o.placeholder = "/" o:depends({ [option_name("transport")] = "httpupgrade" }) --- [[ SplitHTTP部分 ]]-- -o = s:option(Value, option_name("splithttp_host"), translate("SplitHTTP Host")) -o:depends({ [option_name("transport")] = "splithttp" }) +-- [[ XHTTP部分 ]]-- +o = s:option(Value, option_name("xhttp_host"), translate("XHTTP Host")) +o:depends({ [option_name("transport")] = "xhttp" }) -o = s:option(Value, option_name("splithttp_path"), translate("SplitHTTP Path")) +o = s:option(Value, option_name("xhttp_path"), translate("XHTTP Path")) o.placeholder = "/" -o:depends({ [option_name("transport")] = "splithttp" }) +o:depends({ [option_name("transport")] = "xhttp" }) --- [[ Mux ]]-- -o = s:option(Flag, option_name("mux"), translate("Mux")) +-- XHTTP XMUX +o = s:option(Flag, option_name("xhttp_xmux"), "XMUX", translate("Enable XHTTP XMUX. It's not recommended to enable Mux.Cool at the same time.")) +o:depends({ [option_name("transport")] = "xhttp" }) + +o = s:option(Value, option_name("maxConcurrency"), translate("XMUX Max Concurrency")) +o:depends({ [option_name("xhttp_xmux")] = true }) + +o = s:option(Value, option_name("maxConnections"), translate("XMUX Max Connections")) +o:depends({ [option_name("xhttp_xmux")] = true }) + +o = s:option(Value, option_name("cMaxReuseTimes"), translate("XMUX Connection Max Reuse Times")) +o:depends({ [option_name("xhttp_xmux")] = true }) + +o = s:option(Value, option_name("cMaxLifetimeMs"), translate("XMUX Connection Max Lifetime (ms)")) +o:depends({ [option_name("xhttp_xmux")] = true }) + +-- XHTTP 下行 +o = s:option(Flag, option_name("xhttp_download"), string.format('%s', translate("XHTTP download splitting"))) +o:depends({ [option_name("transport")] = "xhttp" }) + +o = s:option(Value, option_name("xhttp_download_address"), string.format('%s', translate("Address"))) +o:depends({ [option_name("xhttp_download")] = true }) + +o = s:option(Value, option_name("xhttp_download_port"), string.format('%s', translate("Port"))) +o:depends({ [option_name("xhttp_download")] = true }) + +o = s:option(Value, option_name("xhttp_download_host"), string.format('%s', "XHTTP Host")) +o:depends({ [option_name("xhttp_download")] = true }) + +o = s:option(Value, option_name("xhttp_download_path"), string.format('%s', "XHTTP Path"), translate("Must be the same as upload path.")) +o.placeholder = "/" +o:depends({ [option_name("xhttp_download")] = true }) + +o = s:option(Flag, option_name("xhttp_download_tls"), string.format('%s', "TLS")) +o:depends({ [option_name("xhttp_download")] = true }) +o.default = 0 + +o = s:option(Flag, option_name("xhttp_download_reality"), string.format('%s', "REALITY")) +o.default = 0 +o:depends({ [option_name("xhttp_download_tls")] = true }) + +o = s:option(ListValue, option_name("xhttp_download_alpn"), string.format('%s', "alpn")) +o.default = "default" +o:value("default", 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({ [option_name("xhttp_download_tls")] = true, [option_name("xhttp_download_reality")] = false }) + +o = s:option(Value, option_name("xhttp_download_tls_serverName"), string.format('%s', translate("Domain"))) +o:depends({ [option_name("xhttp_download_tls")] = true }) + +o = s:option(Value, option_name("xhttp_download_reality_publicKey"), string.format('%s', translate("Public Key"))) +o:depends({ [option_name("xhttp_download_tls")] = true, [option_name("xhttp_download_reality")] = true }) + +o = s:option(Value, option_name("xhttp_download_reality_shortId"), string.format('%s', translate("Short Id"))) +o:depends({ [option_name("xhttp_download_tls")] = true, [option_name("xhttp_download_reality")] = true }) + +o = s:option(Value, option_name("xhttp_download_reality_spiderX"), string.format('%s', "Spider X")) +o.placeholder = "/" +o:depends({ [option_name("xhttp_download_tls")] = true, [option_name("xhttp_download_reality")] = true }) + +o = s:option(Flag, option_name("xhttp_download_utls"), string.format('%s', "uTLS")) +o.default = "0" +o:depends({ [option_name("xhttp_download_tls")] = true, [option_name("xhttp_download_reality")] = false }) + +o = s:option(ListValue, option_name("xhttp_download_fingerprint"), string.format('%s', translate("Finger Print"))) +o:value("chrome") +o:value("firefox") +o:value("edge") +o:value("safari") +o:value("360") +o:value("qq") +o:value("ios") +o:value("android") +o:value("random") +o:value("randomized") +o.default = "chrome" +o:depends({ [option_name("xhttp_download_tls")] = true, [option_name("xhttp_download_utls")] = true }) +o:depends({ [option_name("xhttp_download_tls")] = true, [option_name("xhttp_download_reality")] = true }) + +o = s:option(Flag, option_name("xhttp_download_xmux"), string.format('%s', "XMUX"), translate("Enable XHTTP XMUX. It's not recommended to enable Mux.Cool at the same time.")) +o:depends({ [option_name("xhttp_download")] = true }) + +o = s:option(Value, option_name("download_maxConcurrency"), string.format('%s', translate("XMUX Max Concurrency"))) +o:depends({ [option_name("xhttp_download_xmux")] = true }) + +o = s:option(Value, option_name("download_maxConnections"), string.format('%s', translate("XMUX Max Connections"))) +o:depends({ [option_name("xhttp_download_xmux")] = true }) + +o = s:option(Value, option_name("download_cMaxReuseTimes"), string.format('%s', translate("XMUX Connection Max Reuse Times"))) +o:depends({ [option_name("xhttp_download_xmux")] = true }) + +o = s:option(Value, option_name("download_cMaxLifetimeMs"), string.format('%s', translate("XMUX Connection Max Lifetime (ms)"))) +o:depends({ [option_name("xhttp_download_xmux")] = true }) + +-- [[ Mux.Cool ]]-- +o = s:option(Flag, option_name("mux"), "Mux", translate("Enable Mux.Cool")) o:depends({ [option_name("protocol")] = "vmess" }) o:depends({ [option_name("protocol")] = "vless", [option_name("flow")] = "" }) o:depends({ [option_name("protocol")] = "http" }) @@ -588,7 +688,7 @@ o.default = 8 o:depends({ [option_name("mux")] = true }) -- [[ XUDP Mux ]]-- -o = s:option(Flag, option_name("xmux"), translate("xMux")) +o = s:option(Flag, option_name("xmux"), "XUDP Mux") o.default = 1 o:depends({ [option_name("protocol")] = "vless", [option_name("flow")] = "xtls-rprx-vision" }) o:depends({ [option_name("protocol")] = "vless", [option_name("flow")] = "xtls-rprx-vision-udp443" }) diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua index 09853c7b8..816fa4ef6 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua @@ -110,6 +110,15 @@ function gen_outbound(flag, node, tag, proxy_table) end end + if node.type == "Xray" and node.transport == "xhttp" then + if node.xhttp_download_tls and node.xhttp_download_tls == "1" then + node.xhttp_download_stream_security = "tls" + if node.xhttp_download_reality and node.xhttp_download_reality == "1" then + node.xhttp_download_stream_security = "reality" + end + end + end + if node.protocol == "wireguard" and node.wireguard_reserved then local bytes = {} if not node.wireguard_reserved:match("[^%d,]+") then @@ -159,7 +168,7 @@ function gen_outbound(flag, node, tag, proxy_table) spiderX = node.reality_spiderX or "/", fingerprint = (node.type == "Xray" and node.fingerprint and node.fingerprint ~= "") and node.fingerprint or "chrome" } or nil, - tcpSettings = (node.transport == "tcp" and node.protocol ~= "socks") and { + rawSettings = ((node.transport == "raw" or node.transport == "tcp") and node.protocol ~= "socks") and { header = { type = node.tcp_guise or "none", request = (node.tcp_guise == "http") and { @@ -213,9 +222,35 @@ function gen_outbound(flag, node, tag, proxy_table) path = node.httpupgrade_path or "/", host = node.httpupgrade_host } or nil, - splithttpSettings = (node.transport == "splithttp") and { - path = node.splithttp_path or "/", - host = node.splithttp_host + xhttpSettings = (node.transport == "xhttp" or node.transport == "splithttp") and { + path = node.xhttp_path or node.splithttp_path or "/", + host = node.xhttp_host or node.splithttp_host, + downloadSettings = (node.xhttp_download == "1") and { + address = node.xhttp_download_address, + port = tonumber(node.xhttp_download_port), + network = "xhttp", + xhttpSettings = { + path = node.xhttp_download_path, + host = node.xhttp_download_host, + }, + security = node.xhttp_download_stream_security, + tlsSettings = (node.xhttp_download_stream_security == "tls") and { + serverName = node.xhttp_download_tls_serverName, + allowInsecure = false, + fingerprint = (node.xhttp_download_utls == "1" and + node.xhttp_download_fingerprint and + node.xhttp_download_fingerprint ~= "") and node.xhttp_download_fingerprint or nil + } or nil, + realitySettings = (node.xhttp_download_stream_security == "reality") and { + serverName = node.xhttp_download_tls_serverName, + publicKey = node.xhttp_download_reality_publicKey, + shortId = node.xhttp_download_reality_shortId or "", + spiderX = node.xhttp_download_reality_spiderX or "/", + fingerprint = ( + node.xhttp_download_fingerprint and + node.xhttp_download_fingerprint ~= "") and node.xhttp_download_fingerprint or nil + } or nil, + } or nil } or nil, } or nil, settings = { @@ -280,6 +315,41 @@ function gen_outbound(flag, node, tag, proxy_table) result.streamSettings.tlsSettings.alpn = alpn end end + + local alpn_download = {} + if node.xhttp_download_alpn and node.xhttp_download_alpn ~= "default" then + string.gsub(node.xhttp_download_alpn, '[^' .. "," .. ']+', function(w) + table.insert(alpn_download, w) + end) + end + if alpn_download and #alpn_download > 0 then + if result.streamSettings.xhttpSettings.downloadSettings.tlsSettings then + result.streamSettings.xhttpSettings.downloadSettings.tlsSettings.alpn = alpn_download + end + end + + local xmux = {} + if (node.xhttp_xmux == "1") then + xmux.maxConcurrency = node.maxConcurrency and (string.find(node.maxConcurrency, "-") and node.maxConcurrency or tonumber(node.maxConcurrency)) or 0 + xmux.maxConnections = node.maxConnections and (string.find(node.maxConnections, "-") and node.maxConnections or tonumber(node.maxConnections)) or 0 + xmux.cMaxReuseTimes = node.cMaxReuseTimes and (string.find(node.cMaxReuseTimes, "-") and node.cMaxReuseTimes or tonumber(node.cMaxReuseTimes)) or 0 + xmux.cMaxLifetimeMs = node.cMaxLifetimeMs and (string.find(node.cMaxLifetimeMs, "-") and node.cMaxLifetimeMs or tonumber(node.cMaxLifetimeMs)) or 0 + if result.streamSettings.xhttpSettings then + result.streamSettings.xhttpSettings.xmux = xmux + end + end + + local xmux_download = {} + if (node.xhttp_download_xmux == "1") then + xmux_download.maxConcurrency = node.download_maxConcurrency and (string.find(node.download_maxConcurrency, "-") and node.download_maxConcurrency or tonumber(node.download_maxConcurrency)) or 0 + xmux_download.maxConnections = node.download_maxConnections and (string.find(node.download_maxConnections, "-") and node.download_maxConnections or tonumber(node.download_maxConnections)) or 0 + xmux_download.cMaxReuseTimes = node.download_cMaxReuseTimes and (string.find(node.download_cMaxReuseTimes, "-") and node.download_cMaxReuseTimes or tonumber(node.download_cMaxReuseTimes)) or 0 + xmux_download.cMaxLifetimeMs = node.download_cMaxLifetimeMs and (string.find(node.download_cMaxLifetimeMs, "-") and node.download_cMaxLifetimeMs or tonumber(node.download_cMaxLifetimeMs)) or 0 + if result.streamSettings.xhttpSettings.downloadSettings.xhttpSettings then + result.streamSettings.xhttpSettings.downloadSettings.xhttpSettings.xmux = xmux_download + end + end + end return result end @@ -447,7 +517,7 @@ function gen_config_server(node) } } } or nil, - tcpSettings = (node.transport == "tcp") and { + rawSettings = (node.transport == "raw" or node.transport == "tcp") and { header = { type = node.tcp_guise, request = (node.tcp_guise == "http") and { @@ -672,9 +742,20 @@ function gen_config(var) return inboundTag end - local function gen_balancer(_node, loopbackTag) + local function gen_balancer(_node, loopback_tag) + local balancer_id = _node[".name"] + local balancer_tag = "balancer-" .. balancer_id + local loopback_dst = balancer_id -- route destination for the loopback outbound + if not loopback_tag or loopback_tag == "" then loopback_tag = balancer_id end + -- existing balancer + for _, v in ipairs(balancers) do + if v.tag == balancer_tag then + gen_loopback(loopback_tag, loopback_dst) + return balancer_tag + end + end + -- new balancer local blc_nodes = _node.balancing_node - local fallback_node_id = _node.fallback_node local length = #blc_nodes local valid_nodes = {} for i = 1, length do @@ -698,6 +779,10 @@ function gen_config(var) end end end + if #valid_nodes == 0 then return nil end + + -- fallback node + local fallback_node_id = _node.fallback_node if fallback_node_id == "" then fallback_node_id = nil end if fallback_node_id then local is_new_node = true @@ -718,41 +803,33 @@ function gen_config(var) fallback_node_id = nil end else - local valid = gen_balancer(fallback_node) - if not valid then + if not gen_balancer(fallback_node) then fallback_node_id = nil end end end end - - local valid = nil - if #valid_nodes > 0 then - local balancerTag = get_balancer_tag(_node[".name"]) - table.insert(balancers, { - tag = balancerTag, - selector = valid_nodes, - fallbackTag = fallback_node_id, - strategy = { type = _node.balancingStrategy or "random" } - }) - if _node.balancingStrategy == "leastPing" or fallback_node_id then - if not observatory then - observatory = { - subjectSelector = { "blc-" }, - probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil, - probeInterval = _node.probeInterval or "1m", - enableConcurrency = true - } - end + table.insert(balancers, { + tag = balancer_tag, + selector = valid_nodes, + fallbackTag = fallback_node_id, + strategy = { type = _node.balancingStrategy or "random" } + }) + if _node.balancingStrategy == "leastPing" or fallback_node_id then + if not observatory then + observatory = { + subjectSelector = { "blc-" }, + probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil, + probeInterval = _node.probeInterval or "1m", + enableConcurrency = true + } end - if loopbackTag == nil or loopbackTag =="" then loopbackTag = _node[".name"] end - local inboundTag = gen_loopback(loopbackTag, _node[".name"]) - table.insert(rules, { inboundTag = { inboundTag }, balancerTag = balancerTag }) - valid = true end - return valid + local inbound_tag = gen_loopback(loopback_tag, loopback_dst) + table.insert(rules, { inboundTag = { inbound_tag }, balancerTag = balancer_tag }) + return balancer_tag end - + local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name) if not node or not outbound or not outbounds_table then return nil end local default_outTag = outbound.tag @@ -787,31 +864,25 @@ function gen_config(var) node.port = server_port end if node.protocol == "_shunt" then - local preproxy_enabled = node.preproxy_enabled == "1" - local preproxy_rule_name = "main" - local preproxy_tag = "main" + local preproxy_rule_name = node.preproxy_enabled == "1" and "main" or nil + local preproxy_tag = preproxy_rule_name local preproxy_node_id = node["main_node"] - local preproxy_node = preproxy_enabled and preproxy_node_id and uci:get_all(appname, preproxy_node_id) or nil - if preproxy_node then - preproxy_tag = preproxy_tag .. ":" .. preproxy_node.remarks - end - - local proxy_outboundTag, proxy_balancerTag + local preproxy_outbound_tag, preproxy_balancer_tag + local preproxy_nodes local function gen_shunt_node(rule_name, _node_id) if not rule_name then return nil, nil end if not _node_id then _node_id = node[rule_name] or "nil" end - local rule_outboundTag - local rule_balancerTag if _node_id == "_direct" then - rule_outboundTag = "direct" + return "direct", nil elseif _node_id == "_blackhole" then - rule_outboundTag = "blackhole" + return "blackhole", nil elseif _node_id == "_default" then - rule_outboundTag = "default" + return "default", nil elseif _node_id:find("Socks_") then local socks_id = _node_id:sub(1 + #"Socks_") local socks_node = uci:get_all(appname, socks_id) or nil + local socks_tag if socks_node then local _node = { type = "Xray", @@ -823,27 +894,25 @@ function gen_config(var) } local outbound = gen_outbound(flag, _node, rule_name) if outbound then - table.insert(outbounds, outbound) - rule_outboundTag = outbound.tag + if rule_name == "default" then + table.insert(outbounds, 1, outbound) + else + table.insert(outbounds, outbound) + end + socks_tag = outbound.tag end end + return socks_tag, nil elseif _node_id ~= "nil" then local _node = uci:get_all(appname, _node_id) if not _node then return nil, nil end if api.is_normal_node(_node) then - local use_proxy = preproxy_node and node[rule_name .. "_proxy_tag"] == preproxy_rule_name and _node_id ~= preproxy_node_id - if use_proxy and proxy_balancerTag then - for _, blc_node_id in ipairs(preproxy_node.balancing_node) do - if _node_id == blc_node_id then - use_proxy = false - break - end - end - end + local use_proxy = preproxy_tag and node[rule_name .. "_proxy_tag"] == preproxy_rule_name and _node_id ~= preproxy_node_id + if use_proxy and preproxy_balancer_tag and preproxy_nodes[_node_id] then use_proxy = false end local copied_outbound for index, value in ipairs(outbounds) do - if value["_id"] == _node_id and value["_flag_proxy_tag"] == preproxy_tag then + if value["_id"] == _node_id and value["_flag_proxy_tag"] == (use_proxy and preproxy_tag or nil) then copied_outbound = api.clone(value) break end @@ -851,7 +920,7 @@ function gen_config(var) if copied_outbound then copied_outbound.tag = rule_name .. ":" .. _node.remarks table.insert(outbounds, copied_outbound) - rule_outboundTag = rule_name + return copied_outbound.tag, nil else if use_proxy and (_node.type ~= "Xray" or _node.flow == "xtls-rprx-vision") then new_port = get_new_port() @@ -869,8 +938,8 @@ function gen_config(var) _node.port = new_port table.insert(rules, 1, { inboundTag = {"proxy_" .. rule_name}, - outboundTag = proxy_outboundTag, - balancerTag = proxy_balancerTag + outboundTag = not preproxy_balancer_tag and preproxy_tag or nil, + balancerTag = preproxy_balancer_tag }) end local proxy_table = { @@ -885,33 +954,22 @@ function gen_config(var) end end local outbound = gen_outbound(flag, _node, rule_name, proxy_table) + local outbound_tag if outbound then outbound.tag = outbound.tag .. ":" .. _node.remarks - rule_outboundTag = set_outbound_detour(_node, outbound, outbounds, rule_name) + outbound_tag = set_outbound_detour(_node, outbound, outbounds, rule_name) if rule_name == "default" then table.insert(outbounds, 1, outbound) else table.insert(outbounds, outbound) end end + return outbound_tag, nil end elseif _node.protocol == "_balancing" then - local is_new_balancer = true - rule_balancerTag = get_balancer_tag(_node_id) - for _, v in ipairs(balancers) do - if v.tag == rule_balancerTag then - is_new_balancer = false - gen_loopback(rule_name, _node_id) - break - end - end - if is_new_balancer then - local valid = gen_balancer(_node, rule_name) - if not valid then - rule_balancerTag = nil - end - end + return nil, gen_balancer(_node, rule_name) elseif _node.protocol == "_iface" then + local outbound_tag if _node.iface then local outbound = { protocol = "freedom", @@ -923,26 +981,40 @@ function gen_config(var) } } } + outbound_tag = outbound.tag table.insert(outbounds, outbound) - rule_outboundTag = rule_name sys.call("touch /tmp/etc/passwall2/iface/" .. _node.iface) end + return outbound_tag, nil end end - return rule_outboundTag, rule_balancerTag end - if preproxy_node then - proxy_outboundTag, proxy_balancerTag = gen_shunt_node(preproxy_rule_name, preproxy_node_id) - if not proxy_outboundTag and not proxy_balancerTag then - preproxy_node = nil + if preproxy_tag and preproxy_node_id then + preproxy_outbound_tag, preproxy_balancer_tag = gen_shunt_node(preproxy_rule_name, preproxy_node_id) + if preproxy_balancer_tag then + local _node_id = preproxy_node_id + preproxy_nodes = {} + while _node_id do + _node = uci:get_all(appname, _node_id) + if not _node then break end + if _node.protocol ~= "_balancing" then + preproxy_nodes[_node_id] = true + break + end + local _blc_nodes = _node.balancing_node + for i = 1, #_blc_nodes do preproxy_nodes[_blc_nodes[i]] = true end + _node_id = _node.fallback_node + end + elseif preproxy_outbound_tag then + preproxy_tag = preproxy_outbound_tag end end --default_node local default_node_id = node.default_node or "_direct" local default_outboundTag, default_balancerTag = gen_shunt_node("default", default_node_id) - COMMON.default_outbound_tag = default_outbound_tag - COMMON.default_balancer_tag = default_balancer_tag + COMMON.default_outbound_tag = default_outboundTag + COMMON.default_balancer_tag = default_balancerTag --shunt rule uci:foreach(appname, "shunt_rules", function(e) local outboundTag, balancerTag = gen_shunt_node(e[".name"]) @@ -1053,15 +1125,15 @@ function gen_config(var) } elseif node.protocol == "_balancing" then if node.balancing_node then - local valid = gen_balancer(node) - if valid then - table.insert(rules, { network = "tcp,udp", balancerTag = get_balancer_tag(node_id) }) + local balancer_tag = gen_balancer(node) + if balancer_tag then + table.insert(rules, { network = "tcp,udp", balancerTag = balancer_tag }) end routing = { balancers = balancers, rules = rules } - COMMON.default_balancer_tag = get_balancer_tag(node_id) + COMMON.default_balancer_tag = balancer_tag end elseif node.protocol == "_iface" then if node.iface then @@ -1358,7 +1430,7 @@ function gen_config(var) local default_rule_index = #routing.rules > 0 and #routing.rules or 1 for index, value in ipairs(routing.rules) do - if value["_flag"] == "default" then + if value.ruleTag == "default" then default_rule_index = index break end @@ -1461,7 +1533,7 @@ function gen_config(var) }) end - table.insert(outbounds, { + local direct_outbound = { protocol = "freedom", tag = "direct", settings = { @@ -1472,11 +1544,23 @@ function gen_config(var) mark = 255 } } - }) - table.insert(outbounds, { + } + if COMMON.default_outbound_tag == "direct" then + table.insert(outbounds, 1, direct_outbound) + else + table.insert(outbounds, direct_outbound) + end + + local blackhole_outbound = { protocol = "blackhole", tag = "blackhole" - }) + } + if COMMON.default_outbound_tag == "blackhole" then + table.insert(outbounds, 1, blackhole_outbound) + else + table.insert(outbounds, blackhole_outbound) + end + for index, value in ipairs(config.outbounds) do for k, v in pairs(config.outbounds[index]) do if k:find("_") == 1 then diff --git a/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm b/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm index 4026b2f71..5071216db 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm @@ -368,6 +368,10 @@ local api = require "luci.passwall2.api" v_transport = "splithttp"; params += opt.query("host", dom_prefix + "splithttp_host"); params += opt.query("path", dom_prefix + "splithttp_path"); + } else if (v_transport === "xhttp") { + v_transport = "xhttp"; + params += opt.query("host", dom_prefix + "xhttp_host"); + params += opt.query("path", dom_prefix + "xhttp_path"); } params += "&type=" + v_transport; @@ -1135,6 +1139,9 @@ local api = require "luci.passwall2.api" } else if (queryParam.type === "splithttp") { opt.set(dom_prefix + 'splithttp_host', queryParam.host || ""); opt.set(dom_prefix + 'splithttp_path', queryParam.path || ""); + } else if (queryParam.type === "xhttp") { + opt.set(dom_prefix + 'xhttp_host', queryParam.host || ""); + opt.set(dom_prefix + 'xhttp_path', queryParam.path || ""); } if (m.hash) { diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po index 6f70bcee2..2bf6ca592 100644 --- a/luci-app-passwall2/po/zh-cn/passwall2.po +++ b/luci-app-passwall2/po/zh-cn/passwall2.po @@ -1378,6 +1378,30 @@ msgstr "客户端文件不适合当前设备。" msgid "Can't move new file to path: %s" msgstr "无法移动新文件到:%s" +msgid "XHTTP download splitting" +msgstr "XHTTP 下行分离" + +msgid "Must be the same as upload path." +msgstr "必须与上行 path 相同。" + +msgid "Enable XHTTP XMUX. It's not recommended to enable Mux.Cool at the same time." +msgstr "启用 XHTTP XMUX。不建议与 Mux.Cool 同时启用。" + +msgid "XMUX Max Concurrency" +msgstr "XMUX 连接最大复用流数" + +msgid "XMUX Max Connections" +msgstr "XMUX 最大连接数" + +msgid "XMUX Connection Max Reuse Times" +msgstr "XMUX 连接最多复用次数" + +msgid "XMUX Connection Max Lifetime (ms)" +msgstr "XMUX 连接最大存活时间(ms)" + +msgid "Enable Mux.Cool" +msgstr "启用 Mux.Cool" + msgid "Mux concurrency" msgstr "最大并发连接数" diff --git a/luci-app-passwall2/root/usr/share/passwall2/iptables.sh b/luci-app-passwall2/root/usr/share/passwall2/iptables.sh index 73af69df0..ab8ab8db2 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/iptables.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/iptables.sh @@ -223,10 +223,10 @@ load_acl() { } _acl_list=${TMP_ACL_PATH}/${sid}/rule_list - [ $use_interface = "1" ] && _acl_list=${TMP_ACL_PATH}/${sid}/interface_list + [ "$use_interface" = "1" ] && _acl_list=${TMP_ACL_PATH}/${sid}/interface_list for i in $(cat $_acl_list); do - if [ $use_interface = "0" ]; then + if [ "$use_interface" = "0" ]; then if [ -n "$(echo ${i} | grep '^iprange:')" ]; then _iprange=$(echo ${i} | sed 's#iprange:##g') _ipt_source=$(factor ${_iprange} "-m iprange --src-range") diff --git a/luci-app-passwall2/root/usr/share/passwall2/nftables.sh b/luci-app-passwall2/root/usr/share/passwall2/nftables.sh index 86a27f1bb..db87f2316 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/nftables.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/nftables.sh @@ -277,10 +277,10 @@ load_acl() { } _acl_list=${TMP_ACL_PATH}/${sid}/rule_list - [ $use_interface = "1" ] && _acl_list=${TMP_ACL_PATH}/${sid}/interface_list + [ "$use_interface" = "1" ] && _acl_list=${TMP_ACL_PATH}/${sid}/interface_list for i in $(cat $_acl_list); do - if [ $use_interface = "0" ]; then + if [ "$use_interface" = "0" ]; then if [ -n "$(echo ${i} | grep '^iprange:')" ]; then _iprange=$(echo ${i} | sed 's#iprange:##g') _ipt_source=$(factor ${_iprange} "ip saddr")