diff --git a/lean/default-settings/files/zzz-default-settings b/lean/default-settings/files/zzz-default-settings index a0da8f96..2dfbbe0d 100755 --- a/lean/default-settings/files/zzz-default-settings +++ b/lean/default-settings/files/zzz-default-settings @@ -49,12 +49,12 @@ sed -i '/check_signature/d' /etc/opkg.conf sed -i '/REDIRECT --to-ports 53/d' /etc/firewall.user sed -i '/DISTRIB_REVISION/d' /etc/openwrt_release -echo "DISTRIB_REVISION='R26.02.20'" >> /etc/openwrt_release +echo "DISTRIB_REVISION='R26.05.20'" >> /etc/openwrt_release sed -i '/DISTRIB_DESCRIPTION/d' /etc/openwrt_release echo "DISTRIB_DESCRIPTION='LEDE '" >> /etc/openwrt_release sed -i '/OPENWRT_RELEASE/d' /usr/lib/os-release -echo 'OPENWRT_RELEASE="LEDE R26.04.04"' >> /usr/lib/os-release +echo 'OPENWRT_RELEASE="LEDE R26.05.20"' >> /usr/lib/os-release sed -i '/log-facility/d' /etc/dnsmasq.conf echo "log-facility=/dev/null" >> /etc/dnsmasq.conf diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua index 712b68ab..59f12fe2 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua @@ -43,6 +43,8 @@ local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zer local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'") +local singbox_version = api.get_app_version("sing-box"):match("[^v]+") + o = s:option(ListValue, _n("protocol"), translate("Protocol")) o:value("socks", "Socks") o:value("http", "HTTP") @@ -351,13 +353,26 @@ end if singbox_tags:find("with_quic") then o = s:option(Value, _n("hysteria2_hop"), translate("Port hopping range")) o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).") - o:depends({ [_n("protocol")] = "hysteria2" }) + o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o = s:option(Value, _n("hysteria2_hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5.")) o.datatype = "or(uinteger,portrange)" o.placeholder = "30" o.default = "30" - o:depends({ [_n("protocol")] = "hysteria2" }) + o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) + + if api.compare_versions(singbox_version, ">=", "1.14.0") then + o = s:option(Flag, _n("hysteria2_realms"), translate("Realms")) + o.default = "0" + o:depends({ [_n("protocol")] = "hysteria2"}) + end + + o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") + o:depends({ [_n("hysteria2_realms")] = "1" }) + + o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN")) + o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } + o:depends({ [_n("hysteria2_realms")] = "1" }) o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o.password = true @@ -479,7 +494,7 @@ o.default = "0" o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false }) o:depends({ [_n("protocol")] = "tuic" }) o:depends({ [_n("protocol")] = "hysteria" }) -o:depends({ [_n("protocol")] = "hysteria2" }) +o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o:depends({ [_n("protocol")] = "naive" }) o = s:option(TextValue, _n("ech_config"), translate("ECH Config")) @@ -787,10 +802,14 @@ local protocols = s.fields[_n("protocol")].keylist if #protocols > 0 then for i, v in ipairs(protocols) do if not v:find("^_") then - s.fields[_n("address")]:depends({ [_n("protocol")] = v }) - s.fields[_n("port")]:depends({ [_n("protocol")] = v }) - s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v }) - s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v }) + local depends_condition = { [_n("protocol")] = v } + if v == "hysteria2" then + depends_condition[_n("hysteria2_realms")] = false + end + s.fields[_n("address")]:depends(depends_condition) + s.fields[_n("port")]:depends(depends_condition) + s.fields[_n("domain_resolver")]:depends(depends_condition) + s.fields[_n("domain_strategy")]:depends(depends_condition) end end end @@ -805,6 +824,7 @@ if not load_shunt_options then o:value("1", translate("Preproxy Node")) end o:value("2", translate("Landing Node")) + o:depends({ [_n("hysteria2_realms")] = false }) o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy.")) o1:depends({ [_n("chain_proxy")] = "1" }) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua index d018f625..5fafd25b 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua @@ -55,6 +55,7 @@ o:depends({ [_n("custom")] = false }) o = s:option(Value, _n("port"), translate("Listen Port")) o.datatype = "port" +o.rmempty = false o:depends({ [_n("custom")] = false }) o = s:option(Flag, _n("auth"), translate("Auth")) @@ -154,6 +155,17 @@ if singbox_tags:find("with_quic") then end if singbox_tags:find("with_quic") then + o = s:option(Flag, _n("hysteria2_realms"), translate("Realms")) + o.default = "0" + o:depends({ [_n("protocol")] = "hysteria2"}) + + o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") + o:depends({ [_n("hysteria2_realms")] = "1" }) + + o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN")) + o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } + o:depends({ [_n("hysteria2_realms")] = "1" }) + o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o.password = true o:depends({ [_n("protocol")] = "hysteria2"}) @@ -321,7 +333,7 @@ o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false }) o:depends({ [_n("protocol")] = "naive" }) o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ [_n("protocol")] = "tuic" }) -o:depends({ [_n("protocol")] = "hysteria2" }) +o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o = s:option(TextValue, _n("ech_key"), translate("ECH Key")) o.default = "" diff --git a/luci-app-passwall/luasrc/passwall/api.lua b/luci-app-passwall/luasrc/passwall/api.lua index c4d84bd1..90b31ffa 100644 --- a/luci-app-passwall/luasrc/passwall/api.lua +++ b/luci-app-passwall/luasrc/passwall/api.lua @@ -1595,3 +1595,29 @@ function vps_domain_exclude(domain) if map[domain] then return true end return false end + +function parse_realm_uri(uri) + if type(uri) ~= "string" then return nil end + -- realm://token@server/realm_id?query + local token, server_url, realm_id, query = trim(uri):match("^realm://([^@]+)@([^/]+)/([^?]*)%??(.*)$") + if not token or not server_url or not realm_id then return nil end + realm_id = realm_id:gsub("/+$", "") + local realm = { + token = token, + server_url = server_url, + realm_id = realm_id + } + -- 解析 query 中的 stun= + if query and query ~= "" then + local stun_servers = {} + for key, value in query:gmatch("([^&=?]+)=([^&]+)") do + if key == "stun" and value ~= "" then + stun_servers[#stun_servers + 1] = value + end + end + if #stun_servers > 0 then + realm.stun_servers = stun_servers + end + end + return realm +end diff --git a/luci-app-passwall/luasrc/passwall/util_sing-box.lua b/luci-app-passwall/luasrc/passwall/util_sing-box.lua index 28832533..d98e1905 100644 --- a/luci-app-passwall/luasrc/passwall/util_sing-box.lua +++ b/luci-app-passwall/luasrc/passwall/util_sing-box.lua @@ -103,8 +103,8 @@ function gen_outbound(flag, node, tag, proxy_table) local run_socks_instance = true if proxy_table ~= nil and type(proxy_table) == "table" then proxy_tag = proxy_table.tag or nil - fragment = (proxy_table.fragment and node.protocol ~= "naive") and true or nil - record_fragment = (proxy_table.record_fragment and node.protocol ~= "naive") and true or nil + fragment = (proxy_table.fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil + record_fragment = (proxy_table.record_fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil run_socks_instance = proxy_table.run_socks_instance end @@ -472,6 +472,7 @@ function gen_outbound(flag, node, tag, proxy_table) table.insert(server_ports, range) end end + result.server_port = nil end protocol_table = { server_ports = next(server_ports) and server_ports or nil, @@ -528,6 +529,10 @@ function gen_outbound(flag, node, tag, proxy_table) table.insert(server_ports, range) end end + result.server_port = nil + end + if node.hysteria2_realms then + server_ports = {} end local interval, interval_max if next(server_ports) then @@ -571,7 +576,18 @@ function gen_outbound(flag, node, tag, proxy_table) return (t and t >= 2 and t <= 60) and t or nil end)(node.hysteria2_keep_alive_period), disable_path_mtu_discovery = version_ge_1_14_0 and (tonumber(node.hysteria2_disable_mtu_discovery) == 1) or nil, - tls = tls + tls = tls, + realm = node.hysteria2_realms and (function() + result.server = nil + result.server_port = nil + local realm = api.parse_realm_uri(node.hysteria2_realm_url) + if realm then + realm.server_url = realm.server_url and "https://" .. realm.server_url or nil + realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun + return realm + end + return nil + end)() or nil } end @@ -908,7 +924,17 @@ function gen_config_server(node) } }, ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false, - tls = tls + tls = tls, + realm = node.hysteria2_realms and (function() + local realm = api.parse_realm_uri(node.hysteria2_realm_url) + if realm then + realm.server_url = realm.server_url and "https://" .. realm.server_url or nil + realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun + realm.stun_domain_resolver = "direct" + return realm + end + return nil + end)() or nil } end @@ -2088,7 +2114,7 @@ function gen_config(var) if not value.detour and value.server then value.detour = "direct" end - if value.server and not api.datatypes.hostname(value.server) then + if (value.server and not api.datatypes.hostname(value.server)) and not value.realm then value.domain_resolver = nil end for k, v in pairs(config.outbounds[index]) do