diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile
index 79116653..79fdd5ee 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:=26.6.16
+PKG_VERSION:=26.7.12
PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION)
@@ -25,7 +25,7 @@ PKG_CONFIG_DEPENDS:= \
LUCI_TITLE:=LuCI support for PassWall 2
LUCI_PKGARCH:=all
-LUCI_DEPENDS:=+coreutils +coreutils-base64 +coreutils-nohup +curl \
+LUCI_DEPENDS:=+coreutils +coreutils-base64 +coreutils-nohup +coreutils-timeout +curl \
+ip-full +libuci-lua +lua +luci-compat +luci-lib-jsonc +lyaml +resolveip +tcping \
+geoview +v2ray-geoip +v2ray-geosite \
+unzip
@@ -136,6 +136,7 @@ endef
define Package/$(PKG_NAME)/conffiles
/etc/config/passwall2
/etc/config/passwall2_server
+/usr/share/passwall2/direct_ip
/usr/share/passwall2/domains_excluded
endef
diff --git a/luci-app-passwall2/luasrc/controller/passwall2.lua b/luci-app-passwall2/luasrc/controller/passwall2.lua
index c6cd1a9e..a2bc949a 100644
--- a/luci-app-passwall2/luasrc/controller/passwall2.lua
+++ b/luci-app-passwall2/luasrc/controller/passwall2.lua
@@ -103,6 +103,7 @@ function index()
for com, _ in pairs(coms) do
entry({"admin", "services", appname, "check_" .. com}, call("com_check", com)).leaf = true
entry({"admin", "services", appname, "update_" .. com}, call("com_update", com)).leaf = true
+ entry({"admin", "services", appname, "version_" .. com}, call("com_version", com)).leaf = true
end
--[[Backup]]
@@ -699,6 +700,11 @@ function com_update(comname)
http_write_json(json)
end
+function com_version(comname)
+ local version = api.get_app_version(comname)
+ http_write_json_ok(version)
+end
+
local backup_files = {
"/etc/config/passwall2",
"/etc/config/passwall2_server",
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/app_update.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/app_update.lua
index 5649b016..afdef1b9 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/app_update.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/app_update.lua
@@ -1,19 +1,21 @@
local api = require "luci.passwall2.api"
+local com = require "luci.passwall2.com"
local appname = api.appname
m = Map(appname)
api.set_apply_on_parse(m)
-- [[ App Settings ]]--
-s = m:section(TypedSection, "global_app", translate("App Update"),
- "" ..
- translate("Please confirm that your firmware supports FPU.") ..
- "")
+s = m:section(TypedSection, "global_app", translate("App Update"))
s.anonymous = true
-s:append(Template(appname .. "/app_update/app_version"))
+
+local app_version = Template(appname .. "/app_update/app_version")
+app_version.api = api
+app_version.config = m.config
+app_version.com = com
+s:append(app_version)
local k, v
-local com = require "luci.passwall2.com"
for k, v in pairs(com) do
o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name))
o.default = v.default_path or ("/usr/bin/" .. k)
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua
index e77b1aa1..6195b0bd 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua
@@ -355,11 +355,13 @@ o.default = n + 1080
o.datatype = "port"
o.rmempty = false
+--[[
if has_singbox or has_xray then
o = s2:option(Value, "http_port", "HTTP " .. translate("Listen Port") .. " " .. translate("0 is not use"))
o.default = 0
o.datatype = "port"
end
+]]--
local o_node = s.fields["node"]
local o_socks = s2.fields["node"]
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua
index 0f2ea0fa..6a8732ee 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua
@@ -74,13 +74,14 @@ o:depends("balancing_enable", true)
---- Passwall Inner implement Probe URL
o = s:option(Value, "health_probe_url", translate("Probe URL"))
-o.default = "https://www.google.com/generate_204"
o:value("https://cp.cloudflare.com/", "Cloudflare")
o:value("https://www.gstatic.com/generate_204", "Gstatic")
o:value("https://www.google.com/generate_204", "Google")
o:value("https://www.youtube.com/generate_204", "YouTube")
o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)")
o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)")
+o:value("https://wifi.vivo.com.cn/generate_204", "VIVO (CN)")
+o.default = o.keylist[3]
o.description = translate("The URL used to detect the connection status.")
o:depends("health_check_type", "passwall_logic")
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_list.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_list.lua
index 3265dcd9..745188de 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_list.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_list.lua
@@ -25,6 +25,7 @@ o:value("https://www.google.com/generate_204", "Google")
o:value("https://www.youtube.com/generate_204", "YouTube")
o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)")
o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)")
+o:value("https://wifi.vivo.com.cn/generate_204", "VIVO (CN)")
o.default = o.keylist[3]
-- [[ Add the node via the link ]]--
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
index 89f42efd..b5068ccc 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua
@@ -231,13 +231,8 @@ end
o = s:option(Flag, "boot_update", translate("Update Once on Boot"), translate("Updates the subscription the first time runs automatically after each system boot."))
o.default = 0
----- Enable auto update subscribe
-o = s:option(Flag, "auto_update", translate("Enable auto update subscribe"))
-o.default = 0
-o.rmempty = false
-
----- Week Update
-o = s:option(ListValue, "week_update", translate("Update Mode"))
+o = s:option(ListValue, "update_week_mode", translate("Auto Update Mode"))
+o:value("", translate("Disable"))
o:value(8, translate("Loop Mode"))
o:value(7, translate("Every day"))
o:value(1, translate("Every Monday"))
@@ -247,29 +242,39 @@ o:value(4, translate("Every Thursday"))
o:value(5, translate("Every Friday"))
o:value(6, translate("Every Saturday"))
o:value(0, translate("Every Sunday"))
-o.default = 7
-o:depends("auto_update", true)
-o.rmempty = true
----- Time Update
-o = s:option(ListValue, "time_update", translate("Update Time(every day)"))
-for t = 0, 23 do o:value(t, t .. ":00") end
-o.default = 0
-o:depends("week_update", "0")
-o:depends("week_update", "1")
-o:depends("week_update", "2")
-o:depends("week_update", "3")
-o:depends("week_update", "4")
-o:depends("week_update", "5")
-o:depends("week_update", "6")
-o:depends("week_update", "7")
-o.rmempty = true
+o = s:option(Value, "update_time_mode", translate("Update Time"))
+o:value("0:00")
+for t = 0, 23 do
+ if t == 12 then
+ o:value(t .. ":30")
+ elseif t == 23 then
+ o:value(t .. ":59")
+ else
+ o:value(t .. ":00")
+ end
+end
+o.default = "0:00"
+o.validate = function(self, value)
+ local b = api.is_timehhmm(value)
+ if b then
+ return value
+ end
+ return nil
+end
+o:depends("update_week_mode", "0")
+o:depends("update_week_mode", "1")
+o:depends("update_week_mode", "2")
+o:depends("update_week_mode", "3")
+o:depends("update_week_mode", "4")
+o:depends("update_week_mode", "5")
+o:depends("update_week_mode", "6")
+o:depends("update_week_mode", "7")
----- Interval Update
-o = s:option(ListValue, "interval_update", translate("Update Interval(hour)"))
+o = s:option(ListValue, "update_interval_mode", translate("Update Interval(hour)"))
for t = 1, 24 do o:value(t, t .. " " .. translate("hour")) end
o.default = 2
-o:depends("week_update", "8")
+o:depends("update_week_mode", "8")
o.rmempty = true
o = s:option(ListValue, "access_mode", translate("Subscribe URL Access Method"))
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 25913c94..a715756c 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua
@@ -29,7 +29,9 @@ o.rmempty = true
for index, value in ipairs({"stop", "start", "restart"}) do
o = s:option(ListValue, value .. "_week_mode", translate(value .. " automatically mode"))
o:value("", translate("Disable"))
- o:value(8, translate("Loop Mode"))
+ if value == "restart" then
+ o:value(8, translate("Loop Mode"))
+ end
o:value(7, translate("Every day"))
o:value(1, translate("Every Monday"))
o:value(2, translate("Every Tuesday"))
@@ -39,9 +41,25 @@ for index, value in ipairs({"stop", "start", "restart"}) do
o:value(6, translate("Every Saturday"))
o:value(0, translate("Every Sunday"))
- o = s:option(ListValue, value .. "_time_mode", translate(value .. " Time(Every day)"))
- for t = 0, 23 do o:value(t, t .. ":00") end
- o.default = 0
+ o = s:option(Value, value .. "_time_mode", translate(value .. " Time"))
+ o:value("0:00")
+ for t = 0, 23 do
+ if t == 12 then
+ o:value(t .. ":30")
+ elseif t == 23 then
+ o:value(t .. ":59")
+ else
+ o:value(t .. ":00")
+ end
+ end
+ o.default = "0:00"
+ o.validate = function(self, value)
+ local b = api.is_timehhmm(value)
+ if b then
+ return value
+ end
+ return nil
+ end
o:depends(value .. "_week_mode", "0")
o:depends(value .. "_week_mode", "1")
o:depends(value .. "_week_mode", "2")
@@ -150,8 +168,45 @@ o = s:option(Flag, "accept_icmpv6", translate("Hijacking ICMPv6 (IPv6 PING)"))
o:depends("ipv6_tproxy", true)
o.default = 0
-o = s:option(DynamicList, "force_proxy_lan_ip", translate("Force Proxy LAN IP"), translate("By default, commonly used internal network IP ranges will be connect directly (not entering the core). If you want a certain network range to go through a proxy, please add it here."))
-o.datatype = "or(ipmask4,ipmask6)"
+function clean_text(text)
+ local nbsp = string.char(0xC2, 0xA0)
+ local fullwidth_space = string.char(0xE3, 0x80, 0x80)
+ return text
+ :gsub("\t", " ")
+ :gsub(nbsp, " ")
+ :gsub(fullwidth_space, " ")
+ :gsub("^%s+", "")
+ :gsub("%s+$", "\n")
+ :gsub("\r\n", "\n")
+ :gsub("[ \t]*\n[ \t]*", "\n")
+end
+
+local direct_ip_file = "/usr/share/passwall2/direct_ip"
+o = s:option(TextValue, "direct_ip", translate("Direct IP List"), "" .. translate("These had been joined ip addresses will connect directly (not entering the core).") .. "")
+o.rows = 15
+o.wrap = "off"
+o.cfgvalue = function(self, section)
+ return fs.readfile(direct_ip_file) or ""
+end
+o.write = function(self, section, value)
+ fs.writefile(direct_ip_file, value:gsub("\r\n", "\n"))
+end
+o.remove = function(self, section, value)
+ fs.writefile(direct_ip_file, "")
+end
+o.validate = function(self, value)
+ local ipmasks= {}
+ value = clean_text(value)
+ string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, api.trim(w)) end)
+ for index, ipmask in ipairs(ipmasks) do
+ if ipmask ~= "" and not ipmask:find("^#") and not ipmask:find("^geoip:") then
+ if not ( datatypes.ipmask4(ipmask) or datatypes.ipmask6(ipmask) ) then
+ return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!")
+ end
+ end
+ end
+ return value
+end
if has_xray then
s_xray = m:section(TypedSection, "global_xray", "Xray " .. translate("Settings"))
@@ -170,19 +225,17 @@ if has_xray then
o:value("1-5", "1-5")
o:depends("fragment", true)
- o = s_xray:option(Value, "fragment_length", translate("Fragment Length"), translate("Fragmented packet length (byte)"))
- o.datatype = "or(uinteger,portrange)"
- o.default = "100-200"
+ o = s_xray:option(Value, "fragment_lengths", translate("Fragment Length"), translate("Fragmented packet length (byte)"))
+ o.default = "3-5,6-8,10-20"
o:depends("fragment", true)
- o = s_xray:option(Value, "fragment_delay", translate("Fragment Delay"), translate("Fragmentation interval (ms)"))
- o.datatype = "or(uinteger,portrange)"
+ o = s_xray:option(Value, "fragment_delays", translate("Fragment Delay"), translate("Fragmentation interval (ms)"))
o.default = "10-20"
o:depends("fragment", true)
o = s_xray:option(Value, "fragment_maxSplit", translate("Max Split"), translate("Limit the maximum number of splits."))
o.datatype = "or(uinteger,portrange)"
- o.default = "100-200"
+ o.default = "3-6"
o:depends("fragment", true)
o = s_xray:option(Flag, "noise", translate("Noise"), translate("UDP noise, Under some circumstances it can bypass some UDP based protocol restrictions."))
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua
index 4b32eb00..2fba7319 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua
@@ -35,13 +35,8 @@ o.default = "/usr/share/v2ray/"
o.placeholder = o.default
o.rmempty = false
----- Auto Update
-o = s:option(Flag, "auto_update", translate("Enable auto update rules"))
-o.default = 0
-o.rmempty = false
-
----- Week Update
-o = s:option(ListValue, "week_update", translate("Update Mode"))
+o = s:option(ListValue, "update_week_mode", translate("Auto Update Mode"))
+o:value("", translate("Disable"))
o:value(8, translate("Loop Mode"))
o:value(7, translate("Every day"))
o:value(1, translate("Every Monday"))
@@ -51,29 +46,39 @@ o:value(4, translate("Every Thursday"))
o:value(5, translate("Every Friday"))
o:value(6, translate("Every Saturday"))
o:value(0, translate("Every Sunday"))
-o.default = 7
-o:depends("auto_update", true)
-o.rmempty = true
----- Time Update
-o = s:option(ListValue, "time_update", translate("Update Time(every day)"))
-for t = 0, 23 do o:value(t, t .. ":00") end
-o.default = 0
-o:depends("week_update", "0")
-o:depends("week_update", "1")
-o:depends("week_update", "2")
-o:depends("week_update", "3")
-o:depends("week_update", "4")
-o:depends("week_update", "5")
-o:depends("week_update", "6")
-o:depends("week_update", "7")
-o.rmempty = true
+o = s:option(Value, "update_time_mode", translate("Update Time"))
+o:value("0:00")
+for t = 0, 23 do
+ if t == 12 then
+ o:value(t .. ":30")
+ elseif t == 23 then
+ o:value(t .. ":59")
+ else
+ o:value(t .. ":00")
+ end
+end
+o.default = "0:00"
+o.validate = function(self, value)
+ local b = api.is_timehhmm(value)
+ if b then
+ return value
+ end
+ return nil
+end
+o:depends("update_week_mode", "0")
+o:depends("update_week_mode", "1")
+o:depends("update_week_mode", "2")
+o:depends("update_week_mode", "3")
+o:depends("update_week_mode", "4")
+o:depends("update_week_mode", "5")
+o:depends("update_week_mode", "6")
+o:depends("update_week_mode", "7")
----- Interval Update
-o = s:option(ListValue, "interval_update", translate("Update Interval(hour)"))
+o = s:option(ListValue, "update_interval_mode", translate("Update Interval(hour)"))
for t = 1, 24 do o:value(t, t .. " " .. translate("hour")) end
o.default = 2
-o:depends("week_update", "8")
+o:depends("update_week_mode", "8")
o.rmempty = true
--- The update option is always hidden by JavaScript.
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua
index f3930ed4..59723a56 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua
@@ -128,6 +128,7 @@ o:value("https://www.google.com/generate_204", "Google")
o:value("https://www.youtube.com/generate_204", "YouTube")
o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)")
o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)")
+o:value("https://wifi.vivo.com.cn/generate_204", "VIVO (CN)")
o.default = o.keylist[3]
o:depends("enable_autoswitch", true)
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua
index 8cf95aed..2cbeb54d 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua
@@ -46,6 +46,12 @@ o.rewrite_option = o.option
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
+o.validate = function(self, value)
+ value = api.trim(value)
+ local realm = api.parse_realm_uri(value)
+ if realm then return value end
+ return nil, translate("Invalid Realm URL.")
+end
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
@@ -67,6 +73,20 @@ o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o:depends({ [_n("obfs_type")] = "gecko" })
+o = s:option(Value, _n("obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
+o.datatype = "uinteger"
+o.placeholder = "512"
+o.default = "512"
+o:depends({ [_n("obfs_type")] = "gecko" })
+o.rewrite_option = o.option
+
+o = s:option(Value, _n("obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
+o.datatype = "uinteger"
+o.placeholder = "1200"
+o.default = "1200"
+o:depends({ [_n("obfs_type")] = "gecko" })
+o.rewrite_option = o.option
+
o = s:option(Flag, _n("fast_open"), translate("Fast Open"))
o.default = "0"
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 505d6b62..641b9169 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
@@ -32,10 +32,10 @@ if formvalue_proto then s.val["protocol"] = formvalue_proto end
local arg_select_proto = luci.http.formvalue("select_proto") or ""
local ss_method_list = {
- "none", "plain", "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
+ "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
}
-local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zero" }
+local security_list = { "auto", "aes-128-gcm", "chacha20-poly1305" }
local header_type_list = {
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard", "dns"
@@ -243,7 +243,8 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o:value("https://www.youtube.com/generate_204", "YouTube")
o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)")
o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)")
- o.default = "https://www.google.com/generate_204"
+ o:value("https://wifi.vivo.com.cn/generate_204", "VIVO (CN)")
+ o.default = o.keylist[3]
o.description = translate("The URL used to detect the connection status.")
o = s:option(Value, _n("probeInterval"), translate("Probe Interval"))
@@ -348,6 +349,12 @@ 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.validate = function(self, value)
+ value = api.trim(value)
+ local realm = api.parse_realm_uri(value)
+ if realm then return value end
+ return nil, translate("Invalid Realm URL.")
+end
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" }
@@ -367,6 +374,18 @@ o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
+o.datatype = "uinteger"
+o.placeholder = "512"
+o.default = "512"
+o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+
+o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
+o.datatype = "uinteger"
+o.placeholder = "1200"
+o.default = "1200"
+o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
@@ -493,6 +512,7 @@ o:value("ios")
o:value("android")
o:value("random")
o:value("randomized")
+o:value("unsafe")
o.default = "chrome"
o:depends({ [_n("tls")] = true, [_n("utls")] = true })
o:depends({ [_n("tls")] = true, [_n("reality")] = true })
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua
index 55d75677..e1023ae8 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua
@@ -165,7 +165,8 @@ if load_urltest_options then -- [[ URLTest Start ]]
o:value("https://www.youtube.com/generate_204", "YouTube")
o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)")
o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)")
- o.default = "https://www.gstatic.com/generate_204"
+ o:value("https://wifi.vivo.com.cn/generate_204", "VIVO (CN)")
+ o.default = o.keylist[3]
o.description = translate("The URL used to detect the connection status.")
o = s:option(Value, _n("urltest_interval"), translate("Test interval"))
@@ -411,6 +412,12 @@ if singbox_tags:find("with_quic") then
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.validate = function(self, value)
+ value = api.trim(value)
+ local realm = api.parse_realm_uri(value)
+ if realm then return value end
+ return nil, translate("Invalid Realm URL.")
+ end
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" }
@@ -430,6 +437,18 @@ if singbox_tags:find("with_quic") then
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+ o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
+ o.datatype = "uinteger"
+ o.placeholder = "512"
+ o.default = "512"
+ o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+
+ o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
+ o.datatype = "uinteger"
+ o.placeholder = "1200"
+ o.default = "1200"
+ o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
@@ -679,12 +698,12 @@ o = s:option(Flag, _n("http_h2_health_check"), translate("Health check"))
o:depends({ [_n("tls")] = true, [_n("transport")] = "http" })
o = s:option(Value, _n("http_h2_read_idle_timeout"), translate("Idle timeout"))
-o.default = "10"
-o:depends({ [_n("tls")] = true, [_n("transport")] = "http", [_n("http_h2_health_check")] = true })
+o.default = "15"
+o:depends({ [_n("http_h2_health_check")] = true })
o = s:option(Value, _n("http_h2_health_check_timeout"), translate("Health check timeout"))
o.default = "15"
-o:depends({ [_n("tls")] = true, [_n("transport")] = "http", [_n("http_h2_health_check")] = true })
+o:depends({ [_n("http_h2_health_check")] = true })
-- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host"))
@@ -720,11 +739,11 @@ o = s:option(Flag, _n("grpc_health_check"), translate("Health check"))
o:depends({ [_n("transport")] = "grpc" })
o = s:option(Value, _n("grpc_idle_timeout"), translate("Idle timeout"))
-o.default = "10"
+o.default = "15"
o:depends({ [_n("grpc_health_check")] = true })
o = s:option(Value, _n("grpc_health_check_timeout"), translate("Health check timeout"))
-o.default = "20"
+o.default = "15"
o:depends({ [_n("grpc_health_check")] = true })
o = s:option(Flag, _n("grpc_permit_without_stream"), translate("Permit without stream"))
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua
index cc9a733a..0b0dd1e2 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua
@@ -34,6 +34,12 @@ o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
+o.validate = function(self, value)
+ value = api.trim(value)
+ local realm = api.parse_realm_uri(value)
+ if realm then return value end
+ return nil, translate("Invalid Realm URL.")
+end
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
@@ -57,6 +63,20 @@ o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o:depends({ [_n("obfs_type")] = "gecko" })
+o = s:option(Value, _n("obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
+o.datatype = "uinteger"
+o.placeholder = "512"
+o.default = "512"
+o:depends({ [_n("obfs_type")] = "gecko" })
+o.rewrite_option = o.option
+
+o = s:option(Value, _n("obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
+o.datatype = "uinteger"
+o.placeholder = "1200"
+o.default = "1200"
+o:depends({ [_n("obfs_type")] = "gecko" })
+o.rewrite_option = o.option
+
o = s:option(Flag, _n("udp"), translate("UDP"))
o.default = "1"
o.rewrite_option = o.option
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua
index 31104c2f..cf944c3f 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua
@@ -24,7 +24,7 @@ local function _n(name)
end
local x_ss_method_list = {
- "none", "plain", "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
+ "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
}
local header_type_list = {
@@ -130,6 +130,12 @@ 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.validate = function(self, value)
+ value = api.trim(value)
+ local realm = api.parse_realm_uri(value)
+ if realm then return value end
+ return nil, translate("Invalid Realm URL.")
+end
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" }
@@ -149,6 +155,18 @@ o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
+o.datatype = "uinteger"
+o.placeholder = "512"
+o.default = "512"
+o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+
+o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
+o.datatype = "uinteger"
+o.placeholder = "1200"
+o.default = "1200"
+o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"))
o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" })
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua
index f367a01c..d45dc4ad 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua
@@ -163,6 +163,12 @@ if singbox_tags:find("with_quic") then
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.validate = function(self, value)
+ value = api.trim(value)
+ local realm = api.parse_realm_uri(value)
+ if realm then return value end
+ return nil, translate("Invalid Realm URL.")
+ end
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" }
@@ -182,6 +188,18 @@ if singbox_tags:find("with_quic") then
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+ o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
+ o.datatype = "uinteger"
+ o.placeholder = "512"
+ o.default = "512"
+ o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+
+ o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
+ o.datatype = "uinteger"
+ o.placeholder = "1200"
+ o.default = "1200"
+ o:depends({ [_n("hysteria2_obfs_type")] = "gecko" })
+
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm"))
o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" })
diff --git a/luci-app-passwall2/luasrc/passwall2/api.lua b/luci-app-passwall2/luasrc/passwall2/api.lua
index e3519254..561b7555 100644
--- a/luci-app-passwall2/luasrc/passwall2/api.lua
+++ b/luci-app-passwall2/luasrc/passwall2/api.lua
@@ -367,6 +367,18 @@ function strToTable(str)
return loadstring("return " .. str)()
end
+function is_timehhmm(timeStr)
+ local hour, minute = string.match(timeStr, "^(%d?%d):(%d%d)$")
+ if hour and minute then
+ hour = tonumber(hour)
+ minute = tonumber(minute)
+ if hour >= 0 and hour <= 23 and minute >= 0 and minute <= 59 then
+ return true
+ end
+ end
+ return false
+end
+
function is_normal_node(e)
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
return false
@@ -1449,7 +1461,7 @@ function luci_types(id, m, s, type_name, option_prefix)
end
end
end
-function format_go_time(input)
+function format_go_time(input, default)
input = input and trim(input)
local N = 0
if input and input:match("^%d+$") then
@@ -1467,7 +1479,7 @@ function format_go_time(input)
end
end
if N <= 0 then
- return "0s"
+ return default or "0s"
end
local result = ""
local h = math.floor(N / 3600)
@@ -1610,22 +1622,34 @@ function get_dnsmasq_server_domain()
end
function parse_realm_uri(uri)
- if type(uri) ~= "string" then return nil end
+ uri = trim(uri)
+ if uri == "" then return nil end
-- realm[+http]://token@server/realm_id?query
- local scheme, token, server_url, realm_id, query = trim(uri):match("^(realm%+http|realm)://([^@]+)@([^/]+)/([^?]*)%??(.*)$")
- if not scheme or not token or not server_url or not realm_id then return nil end
+ local scheme = (uri:match("^realm%+http://") and "realm+http") or (uri:match("^realm://") and "realm")
+ if not scheme then return nil end
+ uri = uri:gsub("^realm%+http://", ""):gsub("^realm://", "")
+ local token, server_url, realm_id, query = uri:match("^([^@]+)@([^/]+)/([^?]*)%??(.*)$")
+ if not token or not server_url or not realm_id then return nil end
realm_id = realm_id:gsub("/+$", "")
+ local address, port = server_url:match("^%[([^%]]+)%]:(%d+)$") --ipv6:port
+ if not address then
+ address, port = server_url:match("^([^:]+):(%d+)$") --ipv4[domain]:port
+ end
+ address = address or server_url:match("^%[([^%]]+)%]$") or server_url
+ port = tonumber(port) or (scheme == "realm+http" and 80 or 443)
local realm = {
scheme = scheme,
token = token,
server_url = server_url,
+ address = address,
+ port = port,
realm_id = realm_id
}
-- Parse stun= in the query
local stun_servers
- for value in (query or ""):gmatch("[?&]?[Ss][Tt][Uu][Nn]=([^&]+)") do
- if not stun_servers then stun_servers = {} end
- stun_servers[#stun_servers + 1] = value
+ for v in (query or ""):gmatch("[Ss][Tt][Uu][Nn]=([^&]+)") do
+ stun_servers = stun_servers or {}
+ stun_servers[#stun_servers + 1] = v
end
realm.stun_servers = stun_servers
return realm
diff --git a/luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua b/luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua
index 48673249..de2a1f2d 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua
@@ -39,6 +39,18 @@ function gen_config_server(node)
stunServers = node.hysteria2_realm_stun
} or nil
}
+
+ if config.obfs and config.obfs.gecko then
+ local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512
+ local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200
+ if min <= 0 or min > max or max > 2048 then
+ min = 512
+ max = 1200
+ end
+ config.obfs.gecko.minPacketSize = min
+ config.obfs.gecko.maxPacketSize = max
+ end
+
return config
end
@@ -145,6 +157,17 @@ function gen_config(var)
} or nil
}
+ if config.obfs and config.obfs.gecko then
+ local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512
+ local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200
+ if min <= 0 or min > max or max > 2048 then
+ min = 512
+ max = 1200
+ end
+ config.obfs.gecko.minPacketSize = min
+ config.obfs.gecko.maxPacketSize = max
+ end
+
return jsonc.stringify(config, 1)
end
diff --git a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
index 39580747..bbb5e872 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
@@ -327,8 +327,6 @@ function gen_outbound(flag, node, tag, proxy_table)
headers = node.user_agent and {
["User-Agent"] = node.user_agent
} or nil,
- idle_timeout = (node.http_h2_health_check == "1") and node.http_h2_read_idle_timeout or nil,
- ping_timeout = (node.http_h2_health_check == "1") and node.http_h2_health_check_timeout or nil,
}
-- TLS is not enforced. If TLS is not configured, plain HTTP 1.1 will be used.
end
@@ -341,8 +339,8 @@ function gen_outbound(flag, node, tag, proxy_table)
headers = node.user_agent and {
["User-Agent"] = node.user_agent
} or nil,
- idle_timeout = (node.http_h2_health_check == "1") and node.http_h2_read_idle_timeout or nil,
- ping_timeout = (node.http_h2_health_check == "1") and node.http_h2_health_check_timeout or nil,
+ idle_timeout = (node.http_h2_health_check == "1") and api.format_go_time(node.http_h2_read_idle_timeout, "15s") or nil,
+ ping_timeout = (node.http_h2_health_check == "1") and api.format_go_time(node.http_h2_health_check_timeout, "15s") or nil,
}
-- TLS is not enforced. If TLS is not configured, plain HTTP 1.1 will be used.
end
@@ -382,8 +380,8 @@ function gen_outbound(flag, node, tag, proxy_table)
v2ray_transport = {
type = "grpc",
service_name = node.grpc_serviceName,
- idle_timeout = tonumber(node.grpc_idle_timeout) or nil,
- ping_timeout = tonumber(node.grpc_health_check_timeout) or nil,
+ idle_timeout = (node.grpc_health_check == "1") and api.format_go_time(node.grpc_idle_timeout, "15s") or nil,
+ ping_timeout = (node.grpc_health_check == "1") and api.format_go_time(node.grpc_health_check_timeout, "15s") or nil,
permit_without_stream = (node.grpc_permit_without_stream == "1") and true or nil,
}
end
@@ -595,20 +593,34 @@ function gen_outbound(flag, node, tag, proxy_table)
hop_interval_max = interval_max,
up_mbps = (node.hysteria2_up_mbps and tonumber(node.hysteria2_up_mbps)) and tonumber(node.hysteria2_up_mbps) or nil,
down_mbps = (node.hysteria2_down_mbps and tonumber(node.hysteria2_down_mbps)) and tonumber(node.hysteria2_down_mbps) or nil,
- obfs = node.hysteria2_obfs_type and {
- type = node.hysteria2_obfs_type,
- password = node.hysteria2_obfs_password
- } or nil,
+ obfs = (function(t)
+ if not t or t == "" then return nil end
+ local o = {
+ type = t,
+ password = node.hysteria2_obfs_password
+ }
+ if t == "gecko" then
+ local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512
+ local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200
+ if min <= 0 or min > max or max > 2048 then
+ min = 512
+ max = 1200
+ end
+ o.min_packet_size = min
+ o.max_packet_size = max
+ end
+ return o
+ end)(node.hysteria2_obfs_type),
password = node.hysteria2_auth_password or nil,
idle_timeout = (function(t)
if not version_ge_1_14_0 then return nil end
t = tonumber(tostring(t or "30"):match("^%d+"))
- return (t and t >= 4 and t <= 120) and t or 30
+ return (t and t >= 4 and t <= 120) and t .. "s" or "30s"
end)(node.hysteria2_idle_timeout),
keep_alive_period = (function(t)
if not version_ge_1_14_0 then return nil end
t = tonumber(tostring(t or "0"):match("^%d+"))
- return (t and t >= 2 and t <= 60) and t or nil
+ return (t and t >= 2 and t <= 60) and t .. "s" 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,
@@ -620,6 +632,8 @@ function gen_outbound(flag, node, tag, proxy_table)
realm.server_url = (realm.scheme == "realm+http" and "http://" or "https://") .. realm.server_url
realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun
realm.scheme = nil
+ realm.address = nil
+ realm.port = nil
return realm
end
return nil
@@ -945,10 +959,24 @@ function gen_config_server(node)
protocol_table = {
up_mbps = (node.hysteria2_ignore_client_bandwidth ~= "1" and node.hysteria2_up_mbps and tonumber(node.hysteria2_up_mbps)) and tonumber(node.hysteria2_up_mbps) or nil,
down_mbps = (node.hysteria2_ignore_client_bandwidth ~= "1" and node.hysteria2_down_mbps and tonumber(node.hysteria2_down_mbps)) and tonumber(node.hysteria2_down_mbps) or nil,
- obfs = node.hysteria2_obfs_type and {
- type = node.hysteria2_obfs_type,
- password = node.hysteria2_obfs_password
- } or nil,
+ obfs = (function(t)
+ if not t or t == "" then return nil end
+ local o = {
+ type = t,
+ password = node.hysteria2_obfs_password
+ }
+ if t == "gecko" then
+ local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512
+ local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200
+ if min <= 0 or min > max or max > 2048 then
+ min = 512
+ max = 1200
+ end
+ o.min_packet_size = min
+ o.max_packet_size = max
+ end
+ return o
+ end)(node.hysteria2_obfs_type),
users = {
{
name = "user1",
@@ -963,6 +991,8 @@ function gen_config_server(node)
realm.server_url = (realm.scheme == "realm+http" and "http://" or "https://") .. realm.server_url
realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun
realm.scheme = nil
+ realm.address = nil
+ realm.port = nil
realm.stun_domain_resolver = "direct"
return realm
end
diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
index 8702176e..6bb03c23 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
@@ -14,6 +14,8 @@ local GLOBAL = {
local xray_version = api.get_app_version("xray")
+local xray_min_version = "26.3.27"
+
local function get_domain_excluded()
local path = string.format("/usr/share/%s/domains_excluded", appname)
local content = fs.readfile(path)
@@ -308,10 +310,18 @@ function gen_outbound(flag, node, tag, proxy_table)
local o = {
type = "salamander",
settings = node.hysteria2_obfs_password and {
- password = node.hysteria2_obfs_password,
- packetSize = node.hysteria2_obfs_type == "gecko" and "512-1200" or nil
+ password = node.hysteria2_obfs_password
} or nil
}
+ if node.hysteria2_obfs_type == "gecko" then
+ local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512
+ local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200
+ if min <= 0 or min > max or max > 2048 then
+ min = 512
+ max = 1200
+ end
+ o.settings.packetSize = min .. "-" .. max
+ end
udp[#udp+1] = o
end
if node.hysteria2_realms then
@@ -413,6 +423,14 @@ function gen_outbound(flag, node, tag, proxy_table)
}
}
+ if node.protocol == "hysteria" and node.hysteria2_realms then
+ local realm = api.parse_realm_uri(node.hysteria2_realm_url)
+ if realm then
+ result.settings.address = realm.address
+ result.settings.port = realm.port
+ end
+ end
+
if node.protocol == "wireguard" then
result.settings.noKernelTun = true
if node.finalmask and node.finalmask ~= "" then
@@ -736,10 +754,18 @@ function gen_config_server(node)
local o = {
type = "salamander",
settings = node.hysteria2_obfs_password and {
- password = node.hysteria2_obfs_password,
- packetSize = node.hysteria2_obfs_type == "gecko" and "512-1200" or nil
+ password = node.hysteria2_obfs_password
} or nil
}
+ if node.hysteria2_obfs_type == "gecko" then
+ local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512
+ local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200
+ if min <= 0 or min > max or max > 2048 then
+ min = 512
+ max = 1200
+ end
+ o.settings.packetSize = min .. "-" .. max
+ end
udp[#udp+1] = o
end
if node.hysteria2_realms then
@@ -784,7 +810,10 @@ function gen_config_server(node)
}
},
outbounds = outbounds,
- routing = routing
+ routing = routing,
+ version = {
+ min = xray_min_version
+ }
}
local alpn = {}
@@ -881,13 +910,21 @@ function gen_config(var)
local xray_settings = uci:get_all(appname, "@global_xray[0]") or {}
if xray_settings.fragment == "1" then
- local delay = xray_settings.fragment_delay
+ local lengths, delays = {}, {}
+ api.trim(xray_settings.fragment_lengths):gsub("[^,]+", function(w)
+ w = w:gsub("%s+", "")
+ if w ~= "" then lengths[#lengths+1] = w end
+ end)
+ api.trim(xray_settings.fragment_delays):gsub("[^,]+", function(w)
+ w = w:gsub("%s+", "")
+ if w ~= "" then delays[#delays+1] = w end
+ end)
fragment_table = {
type = "fragment",
settings = {
packets = xray_settings.fragment_packets,
- length = xray_settings.fragment_length,
- delay = delay and (delay:find("-", 1, true) and delay or tonumber(delay)) or nil,
+ lengths = #lengths > 0 and lengths or nil,
+ delays = #delays > 0 and delays or nil,
maxSplit = xray_settings.fragment_maxSplit
}
}
@@ -1971,6 +2008,10 @@ function gen_config(var)
if inbounds or outbounds then
local config = {
+ env = (function()
+ local asset_location = uci:get(appname, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/"
+ return { XRAY_LOCATION_ASSET = asset_location }
+ end)(),
log = {
--access = string.format("/tmp/etc/%s/%s_access.log", appname, "global"),
--error = string.format("/tmp/etc/%s/%s_error.log", appname, "global"),
@@ -2000,6 +2041,9 @@ function gen_config(var)
-- statsInboundUplink = false,
-- statsInboundDownlink = false
-- }
+ },
+ version = {
+ min = xray_min_version
}
}
diff --git a/luci-app-passwall2/luasrc/view/passwall2/app_update/app_version.htm b/luci-app-passwall2/luasrc/view/passwall2/app_update/app_version.htm
index 0dcbb559..7b31c08e 100644
--- a/luci-app-passwall2/luasrc/view/passwall2/app_update/app_version.htm
+++ b/luci-app-passwall2/luasrc/view/passwall2/app_update/app_version.htm
@@ -1,7 +1,7 @@
+
<%
-local api = require "luci.passwall2.api"
-local com = require "luci.passwall2.com"
-local version = {}
+local api = self.api
+local com = self.com
-%>
+