diff --git a/dae/Makefile b/dae/Makefile
index 0188e0ca..6805bff6 100644
--- a/dae/Makefile
+++ b/dae/Makefile
@@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=dae
PKG_VERSION:=2026.08.05
-PKG_RELEASE:=33
+PKG_RELEASE:=34
-PKG_SOURCE:=dae-src-2026.08.05-479f43645df5.tar.gz
+PKG_SOURCE:=dae-src-2026.08.05-fc1a5bf558fd.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=skip
diff --git a/daed/Makefile b/daed/Makefile
index fa2dd429..0baabf94 100644
--- a/daed/Makefile
+++ b/daed/Makefile
@@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=daed
PKG_VERSION:=2026.08.05
-PKG_RELEASE:=44
+PKG_RELEASE:=45
-PKG_SOURCE:=daed-src-2026.08.05-c389546b8c1f.tar.gz
+PKG_SOURCE:=daed-src-2026.08.05-1d87c2949acc.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=skip
diff --git a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js
index 193a7360..8126f133 100644
--- a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js
+++ b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js
@@ -1048,6 +1048,15 @@ return view.extend({
so.depends({type: /^(zerotier|wireguard|masque|openvpn)$/});
so.modalonly = true;
+ so = ss.taboption('field_general', form.ListValue, 'ipstack_congestion_controller', _('IP stack') + ': ' + _('Congestion controller'));
+ so.value('', _('Keep default'));
+ so.value('cubic', _('cubic'));
+ so.value('reno', _('reno'));
+ so.value('bbr', _('bbr'));
+ so.value('bbr3', _('bbr3'));
+ so.depends({ipstack: /^(auto|mips)$/}); // not empty not gvisor
+ so.modalonly = true;
+
so = ss.taboption('field_general', form.ListValue, 'congestion_controller', _('Congestion controller'));
so.default = hm.congestion_controller[0][0];
hm.congestion_controller.forEach((res) => {
@@ -1055,7 +1064,6 @@ return view.extend({
})
so.depends({type: /^(tuic|shadowquic|trusttunnel)$/});
so.depends({type: 'masque', masque_network: /^(|h3-l4proxy)$/});
- so.depends({ipstack: /^(auto|mips)$/}); // not empty not gvisor
so.modalonly = true;
so = ss.taboption('field_general', form.ListValue, 'bbr_profile', _('BBR profile'));
diff --git a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node
index 078a2c78..a9485d1e 100755
--- a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node
+++ b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node
@@ -171,6 +171,14 @@ migrate() {
uci_remove "$CONF" "$1" zerotier_ipstack
uci_set "$CONF" "$1" ipstack "$zerotier_ipstack"
fi
+
+ # congestion_controller -> ipstack_congestion_controller
+ if isDefined "$1" congestion_controller && (isDefined "$1" zerotier_ipstack || isDefined "$1" ipstack); then
+ local congestion_controller
+ config_get congestion_controller "$1" congestion_controller ""
+ uci_remove "$CONF" "$1" congestion_controller
+ uci_set "$CONF" "$1" ipstack_congestion_controller "$congestion_controller"
+ fi
}
config_foreach migrate node
diff --git a/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc b/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc
index 7d6b96af..60442838 100644
--- a/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc
+++ b/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc
@@ -678,12 +678,12 @@ uci.foreach(uciconf, ucinode, (cfg) => {
/* Extra fields */
"ip-stack": cfg.ipstack ? {
mode: cfg.ipstack,
- "congestion-controller": replace(cfg.congestion_controller, 'new_reno', 'reno')
+ "congestion-controller": cfg.ipstack_congestion_controller
} : null,
"udp-over-stream": strToBool(cfg.shadowquic_udp_over_stream || cfg.tuic_udp_over_stream),
"heartbeat-interval": strToInt(cfg.tuic_heartbeat) || null,
"keep-alive-interval": strToInt(cfg.shadowquic_heartbeat) || null,
- "congestion-controller": cfg.type in ['zerotier', 'wireguard', 'masque', 'openvpn'] ? null : cfg.congestion_controller,
+ "congestion-controller": cfg.congestion_controller,
"bbr-profile": cfg.bbr_profile,
"max-open-streams": strToInt(cfg.max_open_streams) || null,
diff --git a/luci-app-modeminfo/Makefile b/luci-app-modeminfo/Makefile
index b5bb013e..375e9394 100644
--- a/luci-app-modeminfo/Makefile
+++ b/luci-app-modeminfo/Makefile
@@ -4,7 +4,7 @@ LUCI_TITLE:=Information dashboard for 3G/LTE dongle
LUCI_DEPENDS:=+modeminfo
PKG_LICENSE:=GPLv3
PKG_VERSION:=0.4.8
-PKG_RELEASE:=11
+PKG_RELEASE:=12
include $(TOPDIR)/feeds/luci/luci.mk
diff --git a/luci-app-modeminfo/README.md b/luci-app-modeminfo/README.md
index ed85080f..fd0f9411 100644
--- a/luci-app-modeminfo/README.md
+++ b/luci-app-modeminfo/README.md
@@ -16,7 +16,7 @@ Supported devices:
- Sierra Wireless EM7455/EM9191
- - HP LT4220
+ - Telit LN940(HP LT4220)/LM960
- Dell DW5821e/DW5829e
@@ -43,7 +43,7 @@ Supported devices:
|modeminfo |common files |
|modeminfo-qmi |Qualcomm MSM Interface support |
|modeminfo-serial-quectel |Quectel modems support |
-|modeminfo-serial-telit |Telit LN940 (HP LT4220) modem support |
+|modeminfo-serial-telit |Telit LN940(HP LT4220)/LM960 modem support |
|modeminfo-serial-huawei |Huawei MU709/ME909/E3372(stick mode, LTE only) modems support|
|modeminfo-serial-sierra |Sierra EM7455/EM9191 modem support |
|modeminfo-serial-simcom |SimCOM modems support |
diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile
index fed4d100..aa7b2245 100644
--- a/luci-app-passwall/Makefile
+++ b/luci-app-passwall/Makefile
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.8.1
-PKG_RELEASE:=211
+PKG_RELEASE:=212
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/acl_config.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/acl_config.lua
index fec29453..9eec259b 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/client/acl_config.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/acl_config.lua
@@ -49,18 +49,6 @@ m.uci:foreach(appname, "socks", function(s)
end
end)
-
-local dynamicList_write = function(self, section, value)
- local new_t = {}
- if type(value) == "table" then
- new_t = api.table_remove_duplicates(value)
- else
- new_t = { value }
- end
- local new_val = table.concat(new_t, " ")
- return DynamicList.write(self, section, new_val)
-end
-
-- [[ ACLs Settings ]]--
s = m:section(NamedSection, cfgid, translate("ACLs"), translate("ACLs"))
s.addremove = false
@@ -173,7 +161,6 @@ sources.validate = function(self, value, t)
return value
end
-sources.write = dynamicList_write
o = s:option(ListValue, "mode", translate("Mode"))
o:value("0", translate("No Proxy"))
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua
index 1cc2b268..5b436eaf 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua
@@ -372,15 +372,6 @@ if api.is_finded("smartdns") then
o.cfgvalue = function(self, section)
return m:get(section, self.option) or {"tcp://1.1.1.1"}
end
- function o.write(self, section, value)
- local new_t = {}
- if type(value) == "table" then
- new_t = api.table_remove_duplicates(value)
- else
- new_t = { value }
- end
- return DynamicList.write(self, section, new_t)
- end
end
o = s:taboption("DNS", ListValue, "xray_dns_mode", translate("Remote DNS") .. " " .. translate("Request protocol"))
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua
index aa8215bc..d980fc17 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua
@@ -156,6 +156,12 @@ o:value("UseIPv6v4", translate("Prefer IPv6"))
o:value("UseIPv4", translate("IPv4 Only"))
o:value("UseIPv6", translate("IPv6 Only"))
+o = s:option(Flag, "allowInsecure", translate("allowInsecure"))
+o.default = "0"
+o.rmempty = false
+o.description = translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.") .. " " ..
+ translate("Used when the node link does not include this parameter.")
+
o = s:option(ListValue, "filter_keyword_mode", translate("Filter keyword Mode"))
o.default = "5"
o:value("0", translate("Close"))
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/shunt_rules.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/shunt_rules.lua
index a02db5f6..6395451c 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/client/shunt_rules.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/shunt_rules.lua
@@ -164,19 +164,6 @@ source.validate = function(self, value, t)
return value
end
-local dynamicList_write = function(self, section, value)
- local new_t = {}
- if type(value) == "table" then
- new_t = api.table_remove_duplicates(value)
- else
- new_t = { value }
- end
- local new_val = table.concat(new_t, " ")
- return DynamicList.write(self, section, new_val)
-end
-
-source.write = dynamicList_write
-
--[[
-- Too low usage rate, hidden
sourcePort = s:option(Value, "sourcePort", translate("Source port"))
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua
index ab44838c..1ca41493 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua
@@ -116,12 +116,12 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end
end
-- 读取旧 DynamicList
- function o.cfgvalue(self, section)
- return m.uci:get_list(appname, section, "balancing_node") or {}
+ function o.custom_cfgvalue(self, section)
+ return table.concat(m:get(section, "balancing_node") or {}, " ")
end
-- 写入保持 DynamicList
function o.custom_write(self, section, value)
- local old = m.uci:get_list(appname, section, "balancing_node") or {}
+ local old = m:get(section, "balancing_node") or {}
local new, set = {}, {}
for v in value:gmatch("%S+") do
new[#new + 1] = v
@@ -129,13 +129,13 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end
for _, v in ipairs(old) do
if not set[v] then
- m.uci:set_list(appname, section, "balancing_node", new)
+ m:set(section, "balancing_node", new)
return
end
set[v] = nil
end
for _ in pairs(set) do
- m.uci:set_list(appname, section, "balancing_node", new)
+ m:set(section, "balancing_node", new)
return
end
end
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 7bcce0ba..61830237 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
@@ -115,12 +115,12 @@ if load_urltest_options then -- [[ URLTest Start ]]
end
end
-- 读取旧 DynamicList
- function o.cfgvalue(self, section)
- return m.uci:get_list(appname, section, "urltest_node") or {}
+ function o.custom_cfgvalue(self, section)
+ return table.concat(m:get(section, "urltest_node") or {}, " ")
end
-- 写入保持 DynamicList
function o.custom_write(self, section, value)
- local old = m.uci:get_list(appname, section, "urltest_node") or {}
+ local old = m:get(section, "urltest_node") or {}
local new, set = {}, {}
for v in value:gmatch("%S+") do
new[#new + 1] = v
@@ -128,13 +128,13 @@ if load_urltest_options then -- [[ URLTest Start ]]
end
for _, v in ipairs(old) do
if not set[v] then
- m.uci:set_list(appname, section, "urltest_node", new)
+ m:set(section, "urltest_node", new)
return
end
set[v] = nil
end
for _ in pairs(set) do
- m.uci:set_list(appname, section, "urltest_node", new)
+ m:set(section, "urltest_node", new)
return
end
end
diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua
index ee7a72bc..e97a5379 100644
--- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua
+++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua
@@ -216,15 +216,6 @@ o:depends({ [_n("reality")] = true })
o = s:option(DynamicList, _n("reality_serverNames"), translate("serverNames"))
o:depends({ [_n("reality")] = true })
-function o.custom_write(self, section, value)
- local new_t = {}
- if type(value) == "table" then
- new_t = api.table_remove_duplicates(value)
- else
- new_t = { value }
- end
- m:set(section, self.option:sub(1 + #option_prefix), new_t)
-end
o = s:option(ListValue, _n("alpn"), translate("alpn"))
o.default = "default"
diff --git a/luci-app-passwall/luasrc/passwall/api.lua b/luci-app-passwall/luasrc/passwall/api.lua
index d026127d..6193e1f9 100644
--- a/luci-app-passwall/luasrc/passwall/api.lua
+++ b/luci-app-passwall/luasrc/passwall/api.lua
@@ -1014,7 +1014,6 @@ local default_file_tree = {
}
local function get_api_json(url)
- local jsonc = require "luci.jsonc"
local gh_proxy = uci_get_type("global_app", "github_proxy", "0")
local return_code, content
if gh_proxy == "1" then
@@ -1345,6 +1344,25 @@ function set_default_cbi()
self.template = appname .. "/cbi/tvalue"
end
end
+ if true then
+ --DynamicList
+ local DynamicList = cbi.DynamicList
+ function DynamicList.write(self, section, value)
+ local new_t = {}
+ if type(value) == "table" then
+ new_t = table_remove_duplicates(value)
+ else
+ new_t = { value }
+ end
+ local new_val
+ if self.cast == "string" then
+ new_val = table.concat(new_t, " ")
+ else
+ new_val = new_t
+ end
+ return cbi.AbstractValue.write(self, section, new_val)
+ end
+ end
end
function return_map(map)
@@ -1369,6 +1387,7 @@ function return_map(map)
end
function luci_types(id, m, s, type_name, option_prefix)
+ local cbi = require "luci.cbi"
local fv_type
local field_type = s.fields["type"]
if field_type then
@@ -1406,11 +1425,25 @@ function luci_types(id, m, s, type_name, option_prefix)
if self.custom_write then
self:custom_write(section, value)
else
+ local new_val = value
+ if util.instanceof(self, cbi.DynamicList) then
+ local new_t = {}
+ if type(value) == "table" then
+ new_t = table_remove_duplicates(value)
+ else
+ new_t = { value }
+ end
+ if self.cast == "string" then
+ new_val = table.concat(new_t, " ")
+ else
+ new_val = new_t
+ end
+ end
if self.rewrite_option then
- m:set(section, self.rewrite_option, value)
+ m:set(section, self.rewrite_option, new_val)
else
if self.option:find(option_prefix) == 1 then
- m:set(section, self.option:sub(1 + #option_prefix), value)
+ m:set(section, self.option:sub(1 + #option_prefix), new_val)
end
end
end
diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po
index 89c5c558..72d7b95a 100644
--- a/luci-app-passwall/po/zh-cn/passwall.po
+++ b/luci-app-passwall/po/zh-cn/passwall.po
@@ -1283,6 +1283,9 @@ msgstr "使用全局配置"
msgid "User-Agent"
msgstr "用户代理(User-Agent)"
+msgid "Used when the node link does not include this parameter."
+msgstr "当节点链接未包含该参数时,将使用此设置。"
+
msgid "Add"
msgstr "添加"
diff --git a/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua b/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua
index fd85c367..285072b8 100644
--- a/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua
+++ b/luci-app-passwall/root/usr/share/passwall/clash_subconverter.lua
@@ -220,7 +220,7 @@ local function encode_vless(node)
local link = "vless://" .. node.uuid .. "@" .. o.server .. ":" .. o.port
local p = {}
- if node.flow then table.insert(p, "flow=" .. urlencode(node.flow)) end
+ if type(node.flow) == "string" and node.flow:sub(1, 5) == "xtls-" then table.insert(p, "flow=" .. urlencode(node.flow)) end
if node.encryption then table.insert(p, "encryption=" .. urlencode(node.encryption)) end
-- TLS
diff --git a/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/luci-app-passwall/root/usr/share/passwall/subscribe.lua
index fa6924ab..842a025d 100755
--- a/luci-app-passwall/root/usr/share/passwall/subscribe.lua
+++ b/luci-app-passwall/root/usr/share/passwall/subscribe.lua
@@ -29,6 +29,7 @@ local has_ssr = api.is_finded("ssr-local") and api.is_finded("ssr-redir")
local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
local has_hysteria2 = api.finded_com("hysteria")
+local DEFAULT_ALLOWINSECURE = true
local DEFAULT_FILTER_KEYWORD_MODE = uci:get(appname, "@global_subscribe[0]", "filter_keyword_mode") or "0"
local DEFAULT_FILTER_KEYWORD_DISCARD_LIST = uci:get(appname, "@global_subscribe[0]", "filter_discard_list") or {}
local DEFAULT_FILTER_KEYWORD_KEEP_LIST = uci:get(appname, "@global_subscribe[0]", "filter_keep_list") or {}
@@ -516,6 +517,7 @@ end
-- 处理数据
local function processData(szType, content, add_mode, group, sub_cfg)
--log(2, content, add_mode, group)
+ local sub_allowinsecure = DEFAULT_ALLOWINSECURE
local sub_ss_type = DEFAULT_SS_TYPE
local sub_trojan_type = DEFAULT_TROJAN_TYPE
local sub_vmess_type = DEFAULT_VMESS_TYPE
@@ -523,6 +525,9 @@ local function processData(szType, content, add_mode, group, sub_cfg)
local sub_hysteria2_type = DEFAULT_HYSTERIA2_TYPE
local sub_hy_up_mbps, sub_hy_down_mbps = 1000, 1000
if sub_cfg then
+ if sub_cfg.allowInsecure and sub_cfg.allowInsecure ~= "1" then
+ sub_allowinsecure = nil
+ end
local ss_type = sub_cfg.ss_type or "global"
if ss_type ~= "global" and core_has[ss_type] then
sub_ss_type = ss_type
@@ -695,7 +700,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.tls_serverName = (info.sni and info.sni ~= "") and info.sni or info.host
result.tls_pinSHA256 = info.pcs
result.tls_CertByName = info.vcn
- result.tls_allowInsecure = info.allowinsecure or info.allowInsecure or info.insecure
+ local insecure = info.allowinsecure or info.allowInsecure or info.insecure
+ result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
else
result.tls = "0"
end
@@ -971,7 +977,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.reality_mldsa65Verify = params.pqv or nil
end
end
- result.tls_allowInsecure = params.allowinsecure or params.allowInsecure or params.insecure
+ local insecure = params.allowinsecure or params.allowInsecure or params.insecure
+ result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.uot = params.udp
else
result.error_msg = "请更换 Xray 或 Sing-Box 来支持 SS 更多的传输方式。"
@@ -1089,7 +1096,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end
result.tls_pinSHA256 = params.pcs
result.tls_CertByName = params.vcn
- result.tls_allowInsecure = params.allowinsecure or params.allowInsecure or params.insecure
+ local insecure = params.allowinsecure or params.allowInsecure or params.insecure
+ result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
end
if not params.type then params.type = "tcp" end
@@ -1344,7 +1352,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.use_mldsa65Verify = (params.pqv and params.pqv ~= "") and "1" or nil
result.reality_mldsa65Verify = params.pqv or nil
end
- result.tls_allowInsecure = params.allowinsecure or params.allowInsecure or params.insecure
+ local insecure = params.allowinsecure or params.allowInsecure or params.insecure
+ result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
end
result.port = port
@@ -1400,7 +1409,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.hysteria_auth_type = "string"
result.hysteria_auth_password = params.auth
result.tls_serverName = params.peer or params.sni or ""
- result.tls_allowInsecure = params.allowinsecure or params.allowInsecure or params.insecure
+ local insecure = params.allowinsecure or params.allowInsecure or params.insecure
+ result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.alpn = params.alpn
result.hysteria_up_mbps = params.upmbps or sub_hy_up_mbps
result.hysteria_down_mbps = params.downmbps or sub_hy_down_mbps
@@ -1445,7 +1455,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.tls_serverName = params.sni
result.tls_pinSHA256 = params.pcs or params.pinsha256
result.tls_CertByName = params.vcn
- result.tls_allowInsecure = params.allowinsecure or params.insecure
+ local insecure = params.allowinsecure or params.insecure
+ result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.hysteria2_up_mbps = params.upmbps or (sub_cfg and sub_hy_up_mbps or nil)
result.hysteria2_down_mbps = params.downmbps or (sub_cfg and sub_hy_down_mbps or nil)
result.hysteria2_hop = params.mport
@@ -1526,7 +1537,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.tuic_alpn = params.alpn or "h3"
result.tuic_congestion_control = params.congestion_control or "cubic"
result.tuic_udp_relay_mode = params.udp_relay_mode or "native"
- result.tls_allowInsecure = params.allowinsecure or params.insecure or params.allow_insecure
+ local insecure = params.allowinsecure or params.insecure or params.allow_insecure
+ result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
elseif szType == "anytls" then
if has_singbox then
result.type = 'sing-box'
@@ -1593,7 +1605,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end
end
result.port = port
- result.tls_allowInsecure = params.allowinsecure or params.insecure
+ local insecure = params.allowinsecure or params.insecure
+ result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
end
elseif szType == 'naive+https' or szType == 'naive+quic' then
if has_singbox then
diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile
index a1629ad8..31be01f2 100644
--- a/luci-app-passwall2/Makefile
+++ b/luci-app-passwall2/Makefile
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.8.7
-PKG_RELEASE:=75
+PKG_RELEASE:=76
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
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 17d67606..2b1b32a7 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua
@@ -171,19 +171,6 @@ node_socks_bind_local:depends({ node = "", ["!reverse"] = true })
s:tab("DNS", translate("DNS"))
-o = s:taboption("DNS", TextValue, "direct_dns_shunt", translate("Direct domain DNS routing"))
-o.description = "
"
-.. "
" .. translate("Subdomain (recommended): Begining with 'domain:' and the rest is a domain. When the targeting domain is exactly the value, or is a subdomain of the value, this rule takes effect. Example: rule 'domain:v2ray.com' matches 'www.v2ray.com', 'v2ray.com', but not 'xv2ray.com'.") .. "
"
-.. "
" .. translate("Full domain: Begining with 'full:' and the rest is a domain. When the targeting domain is exactly the value, the rule takes effect. Example: rule 'domain:v2ray.com' matches 'v2ray.com', but not 'www.v2ray.com'.") .. "
"
-o.rows = 3
-o.wrap = "off"
-
o = s:taboption("DNS", ListValue, "direct_dns_query_strategy", translate("Direct Query Strategy"))
o.default = "UseIP"
o:value("UseIP")
diff --git a/luci-app-passwall2/luasrc/passwall2/api.lua b/luci-app-passwall2/luasrc/passwall2/api.lua
index 27d6485a..804fd092 100644
--- a/luci-app-passwall2/luasrc/passwall2/api.lua
+++ b/luci-app-passwall2/luasrc/passwall2/api.lua
@@ -5,7 +5,6 @@ nixio = require "nixio"
fs = require "nixio.fs"
sys = require "luci.sys"
uci = require"luci.model.uci".cursor()
-cbi = require "luci.cbi"
util = require "luci.util"
datatypes = require "luci.cbi.datatypes"
jsonc = require "luci.jsonc"
@@ -1014,7 +1013,6 @@ local default_file_tree = {
}
function get_api_json(url)
- local jsonc = require "luci.jsonc"
local return_code, content = curl_logic(url, nil, curl_args)
if return_code ~= 0 or content == "" then return {} end
return jsonc.parse(content) or {}
@@ -1387,6 +1385,7 @@ function set_apply_on_parse(map)
end
function set_default_cbi()
+ local cbi = require "luci.cbi"
if true then
--TextValue
local TextValue = cbi.TextValue
@@ -1418,6 +1417,7 @@ function set_default_cbi()
end
function return_map(map)
+ local cbi = require "luci.cbi"
local api = require "luci.passwall2.api"
if true then
-- header
@@ -1438,6 +1438,7 @@ function return_map(map)
end
function luci_types(id, m, s, type_name, option_prefix)
+ local cbi = require "luci.cbi"
local fv_type
local field_type = s.fields["type"]
if field_type then
diff --git a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
index c63b53c4..72c17551 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua
@@ -111,34 +111,6 @@ for i, v in ipairs(cipherSuites) do
end
]]--
-function parseDNS(str)
- local result_dns_server
- -- [proto]://[ip]
- -- [proto]://[ip]:[port]
- -- https://[ip]/[path]
- -- https://[ip]:[port]/[path]
- local _a = api.parseURL(str)
- if _a then
- if _a.protocol == "tcp" or _a.protocol == "udp" or _a.protocol == "https" then
- result_dns_server = {
- type = _a.protocol,
- server = _a.hostname
- }
- if _a.port then
- result_dns_server.server_port = _a.port
- else
- if _a.protocol == "https" then
- result_dns_server.server_port = 443
- else
- result_dns_server.server_port = 53
- end
- end
- result_dns_server.path = _a.pathname
- end
- end
- return result_dns_server
-end
-
function gen_outbound(flag, node, tag, proxy_table)
local result = nil
if node then
@@ -2023,6 +1995,7 @@ function gen_config(var)
else default_dns_flag = "direct"
end
dns.final = default_dns_flag
+ dns.strategy = default_dns_flag == "remote" and remote_strategy or direct_strategy
-- DNS in order of shunt
if dns_domain_rules and #dns_domain_rules > 0 then
@@ -2046,12 +2019,43 @@ function gen_config(var)
dns_rule.server = nil
end
if value.outboundTag == "direct" then
- dns_rule.strategy = direct_strategy
+ local block_rule
+ if direct_strategy == "ipv4_only" then
+ block_rule = api.clone(dns_rule)
+ block_rule.query_type = { "AAAA" }
+ elseif direct_strategy == "ipv6_only" then
+ block_rule = api.clone(dns_rule)
+ block_rule.query_type = { "A" }
+ end
+ if block_rule then
+ block_rule.action = "predefined"
+ block_rule.rcode = "NOERROR"
+ block_rule.disable_cache = nil
+ block_rule.server = nil
+ table.insert(dns.rules, block_rule)
+ end
end
if value.outboundTag ~= "block" and value.outboundTag ~= "direct" then
dns_rule.server = "remote"
dns_rule.rewrite_ttl = 30
- dns_rule.strategy = remote_strategy
+ if true then
+ local block_rule
+ if remote_strategy == "ipv4_only" then
+ block_rule = api.clone(dns_rule)
+ block_rule.query_type = { "AAAA" }
+ elseif remote_strategy == "ipv6_only" then
+ block_rule = api.clone(dns_rule)
+ block_rule.query_type = { "A" }
+ end
+ if block_rule then
+ block_rule.action = "predefined"
+ block_rule.rcode = "NOERROR"
+ block_rule.disable_cache = nil
+ block_rule.server = nil
+ block_rule.rewrite_ttl = nil
+ table.insert(dns.rules, block_rule)
+ end
+ end
dns_rule.client_subnet = remote_dns_client_ip
if value.outboundTag ~= COMMON.default_outbound_tag and remote_server.server and remote_dns_detour ~= "direct" then
local remote_dns_server = api.clone(remote_server)
@@ -2062,9 +2066,13 @@ function gen_config(var)
end
if value.fakedns then
local fakedns_dns_rule = api.clone(dns_rule)
- fakedns_dns_rule.query_type = {
- "A", "AAAA"
- }
+ if remote_strategy == "ipv4_only" then
+ fakedns_dns_rule.query_type = { "A" }
+ elseif remote_strategy == "ipv6_only" then
+ fakedns_dns_rule.query_type = { "AAAA" }
+ else
+ fakedns_dns_rule.query_type = { "A", "AAAA" }
+ end
fakedns_dns_rule.server = fakedns_tag
fakedns_dns_rule.disable_cache = true
fakedns_dns_rule.client_subnet = nil
@@ -2076,23 +2084,26 @@ function gen_config(var)
end
end
if default_dns_flag == "remote" then
+ local dns_rule_query_type = { "A", "AAAA" }
+ if remote_strategy == "ipv4_only" then
+ dns_rule_query_type = { "A" }
+ elseif remote_strategy == "ipv6_only" then
+ dns_rule_query_type = { "AAAA" }
+ end
if remote_dns_fake then
-- When default is not direct and enable fakedns, default DNS use FakeDNS.
local fakedns_dns_rule = {
- query_type = {
- "A", "AAAA"
- },
+ query_type = dns_rule_query_type,
server = fakedns_tag,
disable_cache = true,
- rewrite_ttl = 30,
- strategy = remote_strategy,
+ rewrite_ttl = 30
}
table.insert(dns.rules, fakedns_dns_rule)
else
local remote_dns_rule = {
+ query_type = dns_rule_query_type,
server = "remote",
disable_cache = true,
- strategy = remote_strategy,
}
table.insert(dns.rules, remote_dns_rule)
end
@@ -2328,156 +2339,7 @@ function gen_proto_config(var)
return jsonc.stringify(config, 1)
end
-function gen_front_dns_config(var)
- local dns_listen_port = var["dns_listen_port"]
- local direct_dns_udp_server = var["direct_dns_udp_server"]
- local direct_dns_udp_port = var["direct_dns_udp_port"]
- local direct_dns_query_strategy = var["direct_dns_query_strategy"]
- local default_dns_udp_server = var["default_dns_udp_server"]
- local default_dns_udp_port = var["default_dns_udp_port"]
-
- local dns = {
- servers = {},
- rules = {}
- }
- local inbounds = {}
- local outbounds = {}
- local route = {}
-
- local direct_strategy = "prefer_ipv6"
- if direct_dns_query_strategy == "UseIPv4" then
- direct_strategy = "ipv4_only"
- elseif direct_dns_query_strategy == "UseIPv6" then
- direct_strategy = "ipv6_only"
- end
-
- table.insert(outbounds, {
- type = "direct",
- tag = "direct",
- routing_mark = 255,
- })
-
- local direct_dns_shunt = uci:get(appname, "@global[0]", "direct_dns_shunt") or ""
- if #direct_dns_shunt > 0 then
- local dns_server = {}
- string.gsub(direct_dns_shunt, '[^' .. "\r\n" .. ']+', function(w)
- if w:find("#") == 1 then return end
- local domain = sys.exec(string.format("echo -n $(echo %s | awk -F ' ' '{print $1}')", w))
- local dns = sys.exec(string.format("echo -n $(echo %s | awk -F ' ' '{print $2}')", w))
- if domain ~= "" and dns ~= "" then
- local new_dns_server = parseDNS(dns)
- if new_dns_server then
- if not dns_server[dns] then
- dns_server[dns] = {}
- end
- if not dns_server[dns].server then
- dns_server[dns].server = new_dns_server
- dns_server[dns].server.tag = dns
- dns_server[dns].server.detour = "direct"
- end
- if not dns_server[dns].rule then
- dns_server[dns].rule = {
- action = "route",
- server = dns,
- domain = {},
- domain_suffix = {},
- domain_keyword = {}
- }
- end
- if domain:find("full:") == 1 then
- table.insert(dns_server[dns].rule.domain, domain:sub(1 + #"full:"))
- elseif domain:find("domain:") == 1 then
- table.insert(dns_server[dns].rule.domain_suffix, domain:sub(1 + #"domain:"))
- else
- table.insert(dns_server[dns].rule.domain_keyword, domain)
- end
- end
- end
- end)
- for k, v in pairs(dns_server) do
- table.insert(dns.servers, v.server)
- if #v.rule.domain == 0 then v.rule.domain = nil end
- if #v.rule.domain_suffix == 0 then v.rule.domain_suffix = nil end
- if #v.rule.domain_keyword == 0 then v.rule.domain_keyword = nil end
- table.insert(dns.rules, v.rule)
- end
- end
-
- if direct_dns_udp_server then
- table.insert(dns.servers, {
- tag = "direct",
- type = "udp",
- server = direct_dns_udp_server,
- server_port = tonumber(direct_dns_udp_port) or 53,
- detour = "direct"
- })
- local node_domain = {}
- local nodes_domain_text = sys.exec('uci show passwall2 | grep ".address=" | cut -d "\'" -f 2 | grep "[a-zA-Z]$" | sort -u')
- string.gsub(nodes_domain_text, '[^' .. "\r\n" .. ']+', function(w)
- w = (w or ""):lower()
- table.insert(node_domain, w)
- end)
- if #node_domain > 0 then
- table.insert(dns.rules, {
- action = "route",
- server = "direct",
- domain = node_domain
- })
- end
- route.default_domain_resolver = {
- server = "direct",
- strategy = direct_strategy
- }
- end
-
- if default_dns_udp_server then
- table.insert(dns.servers, {
- tag = "default",
- type = "udp",
- server = default_dns_udp_server,
- server_port = tonumber(default_dns_udp_port) or 53,
- detour = "direct"
- })
- dns.final = "default"
- else
- dns.final = "direct"
- end
-
- local dns_in_inbound = {
- type = "direct",
- tag = "dns-in",
- listen = "127.0.0.1",
- listen_port = tonumber(dns_listen_port),
- }
- table.insert(inbounds, dns_in_inbound)
-
- route.rules = {}
- table.insert(route.rules, {
- action = "hijack-dns",
- inbound = dns_in_inbound.tag
- })
- table.insert(route.rules, {
- action = "sniff",
- inbound = dns_in_inbound.tag
- })
- route.final = "direct"
-
- local config = {
- log = {
- disabled = true,
- level = "debug",
- timestamp = true,
- },
- dns = dns,
- inbounds = inbounds,
- outbounds = outbounds,
- route = route
- }
- return jsonc.stringify(config, 1)
-end
-
_G.gen_config = gen_config
-_G.gen_front_dns_config = gen_front_dns_config
_G.gen_proto_config = gen_proto_config
_G.geo_convert_srs = geo_convert_srs
diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
index 530670c9..1df0c1a8 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
@@ -51,35 +51,6 @@ for i, v in ipairs(cipherSuites) do
end
]]--
-function parseDNS(str)
- local result_dns_server
- -- [proto]://[ip]
- -- [proto]://[ip]:[port]
- -- https://[ip]/[path]
- -- https://[ip]:[port]/[path]
- local _a = api.parseURL(str)
- if _a then
- if _a.protocol == "tcp" or _a.protocol == "udp" or _a.protocol == "https" then
- result_dns_server = {
- address = str
- }
- if _a.protocol == "udp" then
- result_dns_server.address = _a.hostname
- end
- if _a.port then
- result_dns_server.port = _a.port
- else
- if _a.protocol == "https" then
- result_dns_server.port = 443
- else
- result_dns_server.port = 53
- end
- end
- end
- end
- return result_dns_server
-end
-
function gen_outbound(flag, node, tag, proxy_table)
local result = nil
if node then
@@ -2186,157 +2157,7 @@ function gen_proto_config(var)
return jsonc.stringify(config, 1)
end
-function gen_front_dns_config(var)
- local dns_listen_port = var["dns_listen_port"]
- local direct_dns_udp_server = var["direct_dns_udp_server"]
- local direct_dns_udp_port = var["direct_dns_udp_port"]
- local default_dns_udp_server = var["default_dns_udp_server"]
- local default_dns_udp_port = var["default_dns_udp_port"]
-
- local queryStrategy = "UseIP"
- local dns = {
- tag = "dns-global-direct",
- disableCache = false,
- disableFallback = true,
- disableFallbackIfMatch = true,
- queryStrategy = queryStrategy,
- servers = {}
- }
- local inbounds = {}
- local outbounds = {}
- local routing = {
- rules = {}
- }
-
- table.insert(outbounds, {
- tag = "direct",
- protocol = "freedom",
- settings = {
- domainStrategy = queryStrategy
- },
- streamSettings = {
- sockopt = {
- mark = 255
- }
- }
- })
-
- if default_dns_udp_server then
- table.insert(dns.servers, {
- tag = "default",
- address = default_dns_udp_server,
- port = tonumber(default_dns_udp_port) or 53,
- queryStrategy = queryStrategy,
- })
- end
-
- local direct_dns_shunt = uci:get(appname, "@global[0]", "direct_dns_shunt") or ""
- if #direct_dns_shunt > 0 then
- local dns_server = {}
- string.gsub(direct_dns_shunt, '[^' .. "\r\n" .. ']+', function(w)
- if w:find("#") == 1 then return end
- local domain = sys.exec(string.format("echo -n $(echo %s | awk -F ' ' '{print $1}')", w))
- local dns = sys.exec(string.format("echo -n $(echo %s | awk -F ' ' '{print $2}')", w))
- if domain ~= "" and dns ~= "" then
- local new_dns_server = parseDNS(dns)
- if new_dns_server then
- if not dns_server[dns] then
- dns_server[dns] = {}
- end
- dns_server[dns].tag = dns
- dns_server[dns].queryStrategy = queryStrategy
- dns_server[dns].address = new_dns_server.address
- dns_server[dns].port = new_dns_server.port
- dns_server[dns].finalQuery = true
- if not dns_server[dns].domains then
- dns_server[dns].domains = {}
- end
- table.insert(dns_server[dns].domains, domain)
- end
- end
- end)
- for k, v in pairs(dns_server) do
- table.insert(dns.servers, v)
- table.insert(routing.rules, {
- inboundTag = {
- v.tag
- },
- outboundTag = "direct"
- })
- end
- end
-
- if direct_dns_udp_server then
- local node_domain = {}
- local nodes_domain_text = sys.exec('uci show passwall2 | grep ".address=" | cut -d "\'" -f 2 | grep "[a-zA-Z]$" | sort -u')
- string.gsub(nodes_domain_text, '[^' .. "\r\n" .. ']+', function(w)
- w = (w or ""):lower()
- table.insert(node_domain, "full:" .. w)
- end)
- if #node_domain > 0 then
- table.insert(dns.servers, {
- tag = "dns-in-vpslist",
- address = direct_dns_udp_server,
- port = tonumber(direct_dns_udp_port) or 53,
- queryStrategy = queryStrategy,
- domains = node_domain,
- finalQuery = true,
- disableCache = false,
- serveStale = true,
- })
- end
- end
-
- table.insert(inbounds, {
- tag = "dns-in",
- listen = "127.0.0.1",
- port = tonumber(dns_listen_port),
- protocol = "dokodemo-door",
- settings = {
- address = "0.0.0.0",
- network = "tcp,udp"
- }
- })
-
- table.insert(outbounds, {
- tag = "dns-out",
- protocol = "dns",
- proxySettings = {
- tag = "direct"
- },
- settings = {
- address = direct_dns_udp_server,
- port = tonumber(direct_dns_udp_port) or 53,
- network = "udp",
- nonIPQuery = "skip",
- blockTypes = {
- 65
- }
- }
- })
-
- table.insert(routing.rules, {
- inboundTag = {
- "dns-in"
- },
- outboundTag = "dns-out"
- })
-
- local config = {
- log = {
- dnsLog = true,
- loglevel = "debug"
- },
- dns = dns,
- inbounds = inbounds,
- outbounds = outbounds,
- routing = routing
- }
- return jsonc.stringify(config, 1)
-end
-
_G.gen_config = gen_config
-_G.gen_front_dns_config = gen_front_dns_config
_G.gen_proto_config = gen_proto_config
if arg[1] then
diff --git a/luci-app-passwall2/po/fa/passwall2.po b/luci-app-passwall2/po/fa/passwall2.po
index 0cbfa1a0..2e924b52 100644
--- a/luci-app-passwall2/po/fa/passwall2.po
+++ b/luci-app-passwall2/po/fa/passwall2.po
@@ -121,12 +121,6 @@ msgstr "0 به معنای عدم استفاده است"
msgid "Current node: %s"
msgstr "گره فعلی: %s"
-msgid "Direct domain DNS routing"
-msgstr "مسیریابی مستقیم DNS دامنه"
-
-msgid "Please note that the program will not start if the format is incorrect!"
-msgstr "لطفا توجه داشته باشید که اگر قالب نادرست باشد، برنامه شروع نخواهد شد!"
-
msgid "IP:Port mode acceptable, multi value split with english comma."
msgstr "حالت IP:Port قابل قبول است، مقادیر متعدد را با کاما انگلیسی جدا کنید."
diff --git a/luci-app-passwall2/po/ru/passwall2.po b/luci-app-passwall2/po/ru/passwall2.po
index 205665bf..2f7da3ee 100644
--- a/luci-app-passwall2/po/ru/passwall2.po
+++ b/luci-app-passwall2/po/ru/passwall2.po
@@ -118,12 +118,6 @@ msgstr "0 — отключить функцию"
msgid "Current node: %s"
msgstr "Текущий узел: %s"
-msgid "Direct domain DNS routing"
-msgstr "Прямая маршрутизация DNS домена"
-
-msgid "Please note that the program will not start if the format is incorrect!"
-msgstr "Обратите внимание, что программа не запустится, если формат указан неверно!"
-
msgid "IP:Port mode acceptable, multi value split with english comma."
msgstr "Допустим формат IP:Port. Несколько значений указывайте через запятую."
diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po
index e6823a2e..8da8f7f7 100644
--- a/luci-app-passwall2/po/zh-cn/passwall2.po
+++ b/luci-app-passwall2/po/zh-cn/passwall2.po
@@ -118,12 +118,6 @@ msgstr "0为不使用"
msgid "Current node: %s"
msgstr "当前节点:%s"
-msgid "Direct domain DNS routing"
-msgstr "直连域名 DNS 分流"
-
-msgid "Please note that the program will not start if the format is incorrect!"
-msgstr "请注意,格式不正确将无法启动!"
-
msgid "IP:Port mode acceptable, multi value split with english comma."
msgstr "接受 IP:Port 形式的输入,多个以英文逗号分隔。"
diff --git a/luci-app-passwall2/po/zh-tw/passwall2.po b/luci-app-passwall2/po/zh-tw/passwall2.po
index 57fc9b65..b012e0b6 100644
--- a/luci-app-passwall2/po/zh-tw/passwall2.po
+++ b/luci-app-passwall2/po/zh-tw/passwall2.po
@@ -118,12 +118,6 @@ msgstr "0為不使用"
msgid "Current node: %s"
msgstr "當前節點:%s"
-msgid "Direct domain DNS routing"
-msgstr "直連網域 DNS 分流"
-
-msgid "Please note that the program will not start if the format is incorrect!"
-msgstr "請注意,格式不正確將無法啟動!"
-
msgid "IP:Port mode acceptable, multi value split with english comma."
msgstr "接受 IP:Port 形式的輸入,多個以英文逗号分隔。"
diff --git a/luci-app-passwall2/root/usr/share/passwall2/app.sh b/luci-app-passwall2/root/usr/share/passwall2/app.sh
index 6c418286..ea032845 100755
--- a/luci-app-passwall2/root/usr/share/passwall2/app.sh
+++ b/luci-app-passwall2/root/usr/share/passwall2/app.sh
@@ -655,58 +655,13 @@ run_global() {
set_cache_var "ACL_GLOBAL_redir_port" "$REDIR_PORT"
}
-run_front_dns() {
- local switch=0
- direct_dns_shunt=$(config_t_get global direct_dns_shunt)
- direct_dns_shunt=$(echo "${direct_dns_shunt}" | grep -v "^#")
- [ -n "${direct_dns_shunt}" ] && switch=1
- [ "${switch}" == "1" ] && {
- local config_file="${TMP_PATH}/direct_dns.json"
- local log_file="${TMP_PATH}/direct_dns.log"
- log_file="/dev/null"
- local listen_port=$(get_new_port 10553)
- json_init
- json_add_string "dns_listen_port" "${listen_port}"
- json_add_string "direct_dns_udp_server" "${DIRECT_DNS_UDP_SERVER}"
- json_add_string "direct_dns_udp_port" "${DIRECT_DNS_UDP_PORT}"
- json_add_string "direct_dns_query_strategy" "${DIRECT_DNS_QUERY_STRATEGY}"
- [ -n "${ACL_GLOBAL_node}" ] && [ -n "${TUN_DNS_PORT}" ] && {
- json_add_string "default_dns_udp_server" "127.0.0.1"
- json_add_string "default_dns_udp_port" "${TUN_DNS_PORT}"
- }
- local _json_arg="$(json_dump)"
-
- local prefer_core=""
- [ -n "${XRAY_BIN}" ] && prefer_core="xray"
- [ -n "${SINGBOX_BIN}" ] && prefer_core="sing-box"
-
- if [ "${prefer_core}" = "xray" ]; then
- lua $UTIL_XRAY gen_front_dns_config "${_json_arg}" > $config_file
- ln_run 0 "$XRAY_BIN" "xray" "${log_file}" run -c "$config_file"
- elif [ "${prefer_core}" = "sing-box" ]; then
- lua $UTIL_SINGBOX gen_front_dns_config "${_json_arg}" > $config_file
- ln_run 0 "$SINGBOX_BIN" "sing-box" "${log_file}" run -c "$config_file"
- else
- return 1
- fi
-
- FRONT_DNS_SERVER="127.0.0.1"
- FRONT_DNS_PORT="${listen_port}"
- }
-}
-
run_global_dnsmasq() {
- [ -z "${ACL_GLOBAL_node}" ] && [ -z "${FRONT_DNS_PORT}" ] && return
+ [ -z "${ACL_GLOBAL_node}" ] && return
local RUN_NEW_DNSMASQ=1
RUN_NEW_DNSMASQ=${DNS_REDIRECT}
DNSMASQ_DEFAULT_DNS="${AUTO_DNS}"
DNSMASQ_LOCAL_DNS="${LOCAL_DNS:-${AUTO_DNS}}"
DNSMASQ_TUN_DNS="${TUN_DNS}"
- [ -n "${FRONT_DNS_PORT}" ] && {
- DNSMASQ_DEFAULT_DNS="${FRONT_DNS_SERVER}#${FRONT_DNS_PORT}"
- DNSMASQ_LOCAL_DNS="${DNSMASQ_DEFAULT_DNS}"
- DNSMASQ_TUN_DNS="${DNSMASQ_DEFAULT_DNS}"
- }
if [ "${RUN_NEW_DNSMASQ}" == "0" ]; then
#The old logic will be removed in the future.
#Run a copy dnsmasq instance, DNS hijack that don't need a proxy devices.
@@ -1194,7 +1149,6 @@ start() {
fi
mkdir -p ${GLOBAL_ACL_PATH}
[ "$ENABLED_DEFAULT_ACL" == 1 ] && run_global
- run_front_dns
run_global_dnsmasq
[ -n "$USE_TABLES" ] && source $APP_PATH/${USE_TABLES}.sh start
set_cache_var "USE_TABLES" "$USE_TABLES"
diff --git a/luci-theme-footstrap/Makefile b/luci-theme-footstrap/Makefile
index 1ff4ecb2..82de7b5c 100644
--- a/luci-theme-footstrap/Makefile
+++ b/luci-theme-footstrap/Makefile
@@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap
FOOTSTRAP_VERSION?=
ifneq ($(FOOTSTRAP_VERSION),)
PKG_VERSION:=$(FOOTSTRAP_VERSION)
-PKG_RELEASE:=9
+PKG_RELEASE:=10
endif
LUCI_TITLE:=Footstrap Theme
diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js
index 2f62bf5c..5318e358 100644
--- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js
+++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-router.js
@@ -381,6 +381,22 @@ function seed() {
adoptEntry();
}
+/* Does this document already carry the stylesheet a menu.d node names?
+ *
+ * The value is a path under /luci-static/resources (`view/foo/foo.css`), and head.ut prints it with
+ * a `?v=` cache key, so compare the PATH only and by suffix — the theme does not get to assume the
+ * resource base, and a re-hosted sheet keeps its element and its href (fs-sheets.js disables the
+ * , it never removes it), which is exactly the "already here" this asks about. */
+function documentHasSheet(css) {
+ const want = '/' + css.replace(/^\/+/, '');
+ for (const link of document.querySelectorAll('link[rel~="stylesheet"][href]')) {
+ const href = (link.getAttribute('href') || '').split('?')[0];
+ if (href.endsWith(want))
+ return true;
+ }
+ return false;
+}
+
/* Attempt an in-place navigation to `pathname`. Returns true if handled as a
* SPA nav (caller should preventDefault), false to let the browser do a normal
* full navigation. `push` adds a history entry (false when replaying popstate).
@@ -402,6 +418,30 @@ function navigate(pathname, push, kbd) {
const className = tree.viewClassFor(node);
if (!className)
return false;
+
+ /* A PAGE WHOSE STYLESHEET ONLY THE SERVER CAN EMIT IS NOT OURS TO SWAP INTO.
+ *
+ * A menu.d node may name its own sheet (`"css": "view/foo/foo.css"`), and the server links it
+ * from on a full load (partials/head.ut). Nothing here can: a swap replaces #view's
+ * children, it does not re-render a document, so reaching such a page by CLICK would show it
+ * with the app's CSS missing — while reaching the same page by URL or F5 showed it styled. One
+ * page, two appearances, decided by how the user got there.
+ *
+ * So decline, exactly as the poisoned-document bail above does: speed is traded for
+ * correctness, never the other way. It costs ONE full load per such page — after it the
+ * is in the document, this test passes, and every later visit is a swap again (fs-sheets.js
+ * owns the sheet from then on and re-lights it per page).
+ *
+ * Injecting the here instead would work and is deliberately not done: it would put the
+ * theme in charge of fetching and ordering a foreign stylesheet, which is the job fs-sheets.js
+ * exists to keep out of the theme. The server already does it correctly.
+ *
+ * `node.css` reaches the client because /admin/menu serves the dispatcher's own tree and
+ * ui.js's scrubMenu() only rewrites `satisfied`. On a luci-base that predates the `css` schema
+ * entry the property is dropped server-side, so this is simply never true there. */
+ if (typeof node.css === 'string' && node.css !== '' && !documentHasSheet(node.css))
+ return false;
+
const rsegs = res.segs;
/* from here on the navigation is committed */
diff --git a/luci-theme-footstrap/styles/pages/70-syslog.css b/luci-theme-footstrap/styles/pages/70-syslog.css
index 5e1ce6d7..3fe8df4b 100644
--- a/luci-theme-footstrap/styles/pages/70-syslog.css
+++ b/luci-theme-footstrap/styles/pages/70-syslog.css
@@ -15,21 +15,26 @@
* cutting in half. The fields shrink instead of forcing that: `min-width: 0` undoes the field
* box in theme/60-inputs.css for this row only.
*
- * Keyed on the controls' OWN ids rather than on the page: `data-page` differs between the system
- * and the kernel log, and a third-party LogreadBox has neither, while ui.js gives these two rows
- * the same ids wherever the widget is rendered. The inline `margin: 0 5px` LuCI writes on the
- * labels stays as the horizontal spacing — only the ROW gap is ours, so nothing here has to
- * fight an inline style. */
- div:has(> #logFacilitySelect),
- div:has(> #logTextFilter) {
+ * Keyed on the ROW's SHAPE, not on the page and not on the controls' ids. `data-page` differs
+ * between the system and the kernel log and a third-party LogreadBox has neither — but the ids
+ * are no better: only the System Log is built by `tools/views.js`, and Status → Kernel Log is a
+ * view of its own (`view/status/dmesg.js`) whose four rows carry `logFromTime`, `logToTime`,
+ * `invertLogRangeTime`, `invertSeverity` and `invertAscendingSort`, of which an id list written
+ * against LogreadBox catches none — `logFacilitySelect` appears there only as a `for=`, on a
+ * label whose control is a time field. Both views wrap their rows in `#content_syslog`, and a
+ * filter row is exactly one that opens with a `