diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile
index a5536db4..3d8aa3cf 100644
--- a/luci-app-passwall2/Makefile
+++ b/luci-app-passwall2/Makefile
@@ -16,14 +16,11 @@ PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
- CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client \
- CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs \
- CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
LUCI_TITLE:=LuCI support for PassWall 2
@@ -100,17 +97,6 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy
select PACKAGE_naiveproxy
default n
-config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client
- bool "Include Shadowsocks Libev Client"
- select PACKAGE_shadowsocks-libev-ss-local
- select PACKAGE_shadowsocks-libev-ss-redir
- default y
-
-config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server
- bool "Include Shadowsocks Libev Server"
- select PACKAGE_shadowsocks-libev-ss-server
- default n
-
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client
bool "Include Shadowsocks Rust Client"
depends on !i386
@@ -139,12 +125,6 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs
select PACKAGE_simple-obfs-client
default y
-config PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client
- bool "Include tuic-client"
- depends on aarch64||arm||i386||x86_64
- select PACKAGE_tuic-client
- default n
-
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
bool "Include V2ray-Plugin (Shadowsocks Plugin)"
select PACKAGE_v2ray-plugin
diff --git a/luci-app-passwall2/luasrc/controller/passwall2.lua b/luci-app-passwall2/luasrc/controller/passwall2.lua
index 03d0587f..c3520cfc 100644
--- a/luci-app-passwall2/luasrc/controller/passwall2.lua
+++ b/luci-app-passwall2/luasrc/controller/passwall2.lua
@@ -59,6 +59,7 @@ function index()
entry({"admin", "services", appname, "server_user"}, cbi(appname .. "/server/user")).leaf = true
--[[ API ]]
+ entry({"admin", "services", appname, "server_user_update"}, call("server_user_update")).leaf = true
entry({"admin", "services", appname, "server_user_status"}, call("server_user_status")).leaf = true
entry({"admin", "services", appname, "server_user_log"}, call("server_user_log")).leaf = true
entry({"admin", "services", appname, "server_get_log"}, call("server_get_log")).leaf = true
@@ -629,6 +630,23 @@ function rollback_rules()
http_write_json_ok()
end
+function server_user_update()
+ local id = http.formvalue("id") -- Node id
+ local data = http.formvalue("data") -- json new Data
+ if id and data then
+ local data_t = jsonParse(data) or {}
+ if next(data_t) then
+ for k, v in pairs(data_t) do
+ uci:set(appname .. "_server", id, k, v)
+ end
+ api.uci_save(uci, appname .. "_server")
+ http_write_json_ok()
+ return
+ end
+ end
+ http_write_json_error()
+end
+
function server_user_status()
local e = {}
e.index = http.formvalue("index")
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua
index 6f7b1866..b8b77501 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua
@@ -78,103 +78,10 @@ o.rmempty = false
o = s:option(Value, "interface", translate("Source Interface"))
o:value("", translate("All"))
--- Populate with actual kernel network devices instead of UCI interface names,
--- because the backend (nftables iifname / iptables -i) matches kernel device names.
-do
- local nfs = require "nixio.fs"
- local _cursor = require("luci.model.uci").cursor()
- local _sysnet = "/sys/class/net/"
-
- -- Map UCI interface names to their device names and vice versa
- local _iface_to_dev = {}
- local _dev_to_ifaces = {}
- local _iface_proto = {}
- _cursor:foreach("network", "interface", function(sec)
- local name = sec[".name"]
- if name ~= "loopback" then
- _iface_proto[name] = sec.proto
- if sec.device then
- _iface_to_dev[name] = sec.device
- _dev_to_ifaces[sec.device] = _dev_to_ifaces[sec.device] or {}
- table.insert(_dev_to_ifaces[sec.device], name)
- end
- end
- end)
-
- -- Classify device type using sysfs attributes
- local function classify_sysfs(dev)
- if nfs.stat(_sysnet .. dev .. "/bridge", "type") == "dir" then
- return translate("Bridge")
- elseif nfs.stat(_sysnet .. dev .. "/wireless", "type") == "dir" then
- return translate("Wireless Adapter")
- elseif dev:match("^tun") or dev:match("^tap") or dev:match("^wg") or dev:match("^ppp") then
- return translate("Tunnel Interface")
- else
- return translate("Ethernet Adapter")
- end
- end
-
- -- Classify offline UCI interfaces by config hints
- local function classify_uci(dev_name, proto)
- if dev_name and dev_name:match("^br%-") then
- return translate("Bridge")
- elseif proto == "wireguard" or proto == "pppoe" or proto == "pptp" or proto == "l2tp" then
- return translate("Tunnel Interface")
- else
- return translate("Interface")
- end
- end
-
- local _seen = {}
- local _devices = {}
-
- -- Active kernel devices from /sys/class/net/.
- -- Skip bridge member ports (/master) and DSA master devices (/dsa) because
- -- nftables iifname matches the parent bridge for routed traffic, not
- -- individual member ports. Also skip internal virtual devices.
- local _iter = nfs.dir(_sysnet)
- if _iter then
- for dev in _iter do
- if dev ~= "lo"
- and not dev:match("^veth")
- and not dev:match("^ifb")
- and not dev:match("^gre")
- and not dev:match("^sit")
- and not dev:match("^ip6tnl")
- and not dev:match("^erspan")
- and not nfs.stat(_sysnet .. dev .. "/master", "type")
- and not nfs.stat(_sysnet .. dev .. "/dsa", "type")
- then
- local dtype = classify_sysfs(dev)
- local label = dtype .. ': "' .. dev .. '"'
- if _dev_to_ifaces[dev] then
- label = label .. " (" .. table.concat(_dev_to_ifaces[dev], ", ") .. ")"
- end
- _devices[#_devices + 1] = { name = dev, label = label, sort = dtype .. ":" .. dev }
- _seen[dev] = true
- end
- end
- end
-
- -- UCI interfaces whose device does not currently exist (down tunnels, VPNs, etc.).
- -- Stored by UCI name since the kernel device is not available yet.
- -- Dedup by device: if two interfaces share a device, only one is shown.
- for iface, dev in pairs(_iface_to_dev) do
- if not _seen[dev] then
- local dtype = classify_uci(dev, _iface_proto[iface])
- local label = dtype .. ': "' .. iface .. '"'
- -- Sort offline entries after active devices
- _devices[#_devices + 1] = { name = iface, label = label, sort = "zzz:" .. iface }
- _seen[dev] = true
- end
- end
-
- table.sort(_devices, function(a, b) return a.sort < b.sort end)
- for _, d in ipairs(_devices) do
- o:value(d.name, d.label)
- end
+local iface = api.get_network_devices()
+for _, d in ipairs(iface) do
+ o:value(d.name, d.label)
end
-
o.validate = function(self, value, section)
if value == "" or value:match("^[a-zA-Z0-9][a-zA-Z0-9%.%_%-]*$") then
return value
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua
index 28e3aa7d..5051bd7b 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua
@@ -14,6 +14,7 @@ formvalue_key = "cbid." .. appname .. "." .. arg[1] .. "."
local header = Template(appname .. "/node_config/header")
header.api = api
+header.config = m.config
header.section = arg[1]
m:append(header)
@@ -91,6 +92,7 @@ end
local footer = Template(appname .. "/node_config/footer")
footer.api = api
+footer.config = m.config
footer.section = arg[1]
m:append(footer)
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua
index a13e46ee..7140ac5c 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua
@@ -1,7 +1,6 @@
local api = require "luci.passwall2.api"
local appname = api.appname
local uci = api.uci
-local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
@@ -12,10 +11,6 @@ local vmess_type = {}
local vless_type = {}
local hysteria2_type = {}
local xray_version = api.get_app_version("xray")
-if has_ss then
- local s = "shadowsocks-libev"
- table.insert(ss_type, s)
-end
if has_ss_rust then
local s = "shadowsocks-rust"
table.insert(ss_type, s)
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 6be6bacf..1cc68769 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
@@ -15,7 +15,6 @@ end
m:append(Template(appname .. "/cbi/nodes_listvalue_com"))
-local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
@@ -26,10 +25,6 @@ local vmess_type = {}
local vless_type = {}
local hysteria2_type = {}
local xray_version = api.get_app_version("xray")
-if has_ss then
- local s = "shadowsocks-libev"
- table.insert(ss_type, s)
-end
if has_ss_rust then
local s = "shadowsocks-rust"
table.insert(ss_type, s)
@@ -223,6 +218,14 @@ if #hysteria2_type > 0 then
for key, value in pairs(hysteria2_type) do
o:value(value)
end
+
+ o = s:option(Value, "hysteria_up_mbps", "Hy/Hy2 " .. translate("Max upload Mbps"))
+ o.datatype = "uinteger"
+ o.default = "100"
+
+ o = s:option(Value, "hysteria_down_mbps", "Hy/Hy2 " .. translate("Max download Mbps"))
+ o.datatype = "uinteger"
+ o.default = "100"
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."))
@@ -287,6 +290,7 @@ o = s:option(ListValue, "chain_proxy", translate("Chain Proxy"))
o:value("", translate("Close(Not use)"))
o:value("1", translate("Preproxy Node"))
o:value("2", translate("Landing Node"))
+o:value("3", translate("Outbound Interface"))
local descrStr = "Chained proxy works only with Xray or Sing-box nodes. "
descrStr = descrStr .. "You can only use manual or imported nodes as chained nodes."
@@ -304,6 +308,14 @@ o2.description = descrStr
o2.template = appname .. "/cbi/nodes_listvalue"
o2.group = {}
+o3 = s:option(Value, "outbound_iface", translate("Outbound Interface"))
+o3:depends({ ["chain_proxy"] = "3" })
+o3:value("", translate("All"))
+local iface = api.get_network_devices()
+for _, d in ipairs(iface) do
+ o3:value(d.name, d.label)
+end
+
for k, v in pairs(nodes_table) do
if (v.type == "Xray" or v.type == "sing-box") and (not v.chain_proxy or v.chain_proxy == "") and v.add_mode ~= "2" then
o1:value(v.id, v.remark)
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 012f4952..f1264f1f 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
@@ -78,6 +78,7 @@ if not arg_select_proto:find("_") then
load_normal_options = true
end
+local netdev_list = api.get_network_devices()
local node_list = api.get_node_list()
local fallback_list = {}
local is_balancer = nil
@@ -270,8 +271,10 @@ end -- [[ Load balancing End ]]
if load_iface_options then -- [[ Custom Interface Start ]]
o = s:option(Value, _n("iface"), translate("Interface"))
- o.default = "eth1"
o:depends({ [_n("protocol")] = "_iface" })
+ for _, d in ipairs(netdev_list) do
+ o:value(d.name, d.label)
+ end
end -- [[ Custom Interface End ]]
@@ -793,6 +796,7 @@ if not load_shunt_options then
if not (load_iface_options or load_balancing_options) then
-- Special node cannot be use pre-proxy.
o:value("1", translate("Preproxy Node"))
+ o:value("3", translate("Outbound Interface"))
end
o:value("2", translate("Landing Node"))
@@ -801,6 +805,13 @@ if not load_shunt_options then
o1.template = appname .. "/cbi/nodes_listvalue"
o1.group = {}
+ o3 = s:option(Value, _n("outbound_iface"), translate("Outbound Interface"))
+ o3:depends({ [_n("chain_proxy")] = "3" })
+ o3:value("", translate("All"))
+ for _, d in ipairs(netdev_list) do
+ o3:value(d.name, d.label)
+ end
+
o2 = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Only support a layer of proxy."))
o2:depends({ [_n("chain_proxy")] = "2" })
o2.template = appname .. "/cbi/nodes_listvalue"
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 cfb5fb61..a3c591ca 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
@@ -94,6 +94,7 @@ if not arg_select_proto:find("_") then
load_normal_options = true
end
+local netdev_list = api.get_network_devices()
local node_list = api.get_node_list()
if load_urltest_options then -- [[ URLTest Start ]]
@@ -198,8 +199,10 @@ end -- [[ URLTest End ]]
if load_iface_options then -- [[ Custom Interface Start ]]
o = s:option(Value, _n("iface"), translate("Interface"))
- o.default = "eth1"
o:depends({ [_n("protocol")] = "_iface" })
+ for _, d in ipairs(netdev_list) do
+ o:value(d.name, d.label)
+ end
end -- [[ Custom Interface End ]]
@@ -875,8 +878,11 @@ if not load_shunt_options then
o1.group = {}
o3 = s:option(Value, _n("outbound_iface"), translate("Outbound Interface"))
- o3.default = "eth1"
o3:depends({ [_n("chain_proxy")] = "3" })
+ o3:value("", translate("All"))
+ for _, d in ipairs(netdev_list) do
+ o3:value(d.name, d.label)
+ end
o2 = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Only support a layer of proxy."))
o2:depends({ [_n("chain_proxy")] = "2", [_n("hysteria2_realms")] = false })
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss.lua
deleted file mode 100644
index b1abf4a5..00000000
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss.lua
+++ /dev/null
@@ -1,62 +0,0 @@
-local m, s = ...
-
-if not api.is_finded("ss-local") then
- return
-end
-
-type_name = "SS"
-
--- [[ Shadowsocks Libev ]]
-
-s.fields["type"]:value(type_name, "Shadowsocks Libev")
-
-if s.val["type"] ~= type_name then
- return
-end
-
-local option_prefix = "ss_"
-
-local function _n(name)
- return option_prefix .. name
-end
-
-local ss_encrypt_method_list = {
- "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
- "aes-192-ctr", "aes-256-ctr", "bf-cfb", "salsa20", "chacha20", "chacha20-ietf",
- "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
- "xchacha20-ietf-poly1305"
-}
-
-o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
-
-o = s:option(Value, _n("port"), translate("Port"))
-o.datatype = "port"
-
-o = s:option(Value, _n("password"), translate("Password"))
-o.password = true
-
-o = s:option(Value, _n("method"), translate("Encrypt Method"))
-for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end
-
-o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
-o.datatype = "uinteger"
-o.default = 300
-
-o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
-o.default = 0
-
-o = s:option(Flag, _n("plugin_enabled"), translate("plugin"))
-o.default = 0
-
-o = s:option(ListValue, _n("plugin"), "SIP003 " .. translate("plugin"))
-o.default = "none"
-o:value("none", translate("none"))
-if api.is_finded("xray-plugin") then o:value("xray-plugin") end
-if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end
-if api.is_finded("obfs-local") then o:value("obfs-local") end
-o:depends({ [_n("plugin_enabled")] = true })
-
-o = s:option(Value, _n("plugin_opts"), translate("opts"))
-o:depends({ [_n("plugin_enabled")] = true })
-
-api.luci_types(arg[1], m, s, type_name, option_prefix)
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/tuic.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/tuic.lua
deleted file mode 100644
index fb7f91ac..00000000
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/tuic.lua
+++ /dev/null
@@ -1,145 +0,0 @@
-local m, s = ...
-
-if not api.is_finded("tuic-client") then
- return
-end
-
-type_name = "TUIC"
-
--- [[ TUIC ]]
-
-s.fields["type"]:value(type_name, "TUIC")
-
-if s.val["type"] ~= type_name then
- return
-end
-
-local option_prefix = "tuic_"
-
-local function _n(name)
- return option_prefix .. name
-end
-
-o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
-
-o = s:option(Value, _n("port"), translate("Port"))
-o.datatype = "port"
-
-o = s:option(Value, _n("uuid"), translate("ID"))
-o.password = true
-
--- Tuic Password for remote server connect
-o = s:option(Value, _n("password"), translate("TUIC User Password For Connect Remote Server"))
-o.password = true
-o.rmempty = true
-o.default = ""
-o.rewrite_option = o.option
-
---[[
--- Tuic username for local socks connect
-o = s:option(Value, _n("socks_username"), translate("TUIC UserName For Local Socks"))
-o.rmempty = true
-o.default = ""
-o.rewrite_option = o.option
-
--- Tuic Password for local socks connect
-o = s:option(Value, _n("socks_password"), translate("TUIC Password For Local Socks"))
-o.password = true
-o.rmempty = true
-o.default = ""
-o.rewrite_option = o.option
---]]
-
-o = s:option(Value, _n("ip"), translate("Set the TUIC proxy server ip address"))
-o.datatype = "ipaddr"
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(ListValue, _n("udp_relay_mode"), translate("UDP relay mode"))
-o:value("native", translate("native"))
-o:value("quic", translate("QUIC"))
-o.default = "native"
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(ListValue, _n("congestion_control"), translate("Congestion control algorithm"))
-o:value("bbr", translate("BBR"))
-o:value("cubic", translate("CUBIC"))
-o:value("new_reno", translate("New Reno"))
-o.default = "cubic"
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Value, _n("heartbeat"), translate("Heartbeat interval(second)"))
-o.datatype = "uinteger"
-o.default = "3"
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Value, _n("timeout"), translate("Timeout for establishing a connection to server(second)"))
-o.datatype = "uinteger"
-o.default = "8"
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Value, _n("gc_interval"), translate("Garbage collection interval(second)"))
-o.datatype = "uinteger"
-o.default = "3"
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Value, _n("gc_lifetime"), translate("Garbage collection lifetime(second)"))
-o.datatype = "uinteger"
-o.default = "15"
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Value, _n("send_window"), translate("TUIC send window"))
-o.datatype = "uinteger"
-o.default = 20971520
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Value, _n("receive_window"), translate("TUIC receive window"))
-o.datatype = "uinteger"
-o.default = 10485760
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Value, _n("max_package_size"), translate("TUIC Maximum packet size the socks5 server can receive from external, in bytes"))
-o.datatype = "uinteger"
-o.default = 1500
-o.rmempty = true
-o.rewrite_option = o.option
-
---Tuic settings for the local inbound socks5 server
-o = s:option(Flag, _n("dual_stack"), translate("Set if the listening socket should be dual-stack"))
-o.default = 0
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Flag, _n("disable_sni"), translate("Disable SNI"))
-o.default = 0
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(Flag, _n("zero_rtt_handshake"), translate("Enable 0-RTT QUIC handshake"))
-o.default = 0
-o.rmempty = true
-o.rewrite_option = o.option
-
-o = s:option(ListValue, _n("tls_alpn"), translate("TLS ALPN"))
-o.rmempty = true
-o.default = ""
-o:value("", translate("Default"))
-o:value("h3")
-o:value("h2")
-o:value("h3,h2")
-o:value("http/1.1")
-o:value("h2,http/1.1")
-o:value("h3,h2,http/1.1")
-o:value("spdy/3.1")
-o:value("h3,spdy/3.1")
-o.rewrite_option = o.option
-
-api.luci_types(arg[1], m, s, type_name, option_prefix)
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 74f5a37d..89742db8 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
@@ -1,28 +1,29 @@
local m, s = ...
-local api = require "luci.passwall2.api"
-
if not api.finded_com("hysteria") then
return
end
local type_name = "Hysteria2"
+-- [[ Hysteria2 ]]
+
+s.fields["type"]:value(type_name, "Hysteria2")
+
+if s.val["type"] and s.val["type"] ~= type_name then
+ return
+end
+
local option_prefix = "hysteria2_"
local function _n(name)
return option_prefix .. name
end
--- [[ Hysteria2 ]]
-
-s.fields["type"]:value(type_name, "Hysteria2")
-
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
-o.rmempty = false
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("realms"), translate("Realms"))
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 3a4d9ba3..ff99b15b 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
@@ -1,13 +1,22 @@
local m, s = ...
-local api = require "luci.passwall2.api"
-
if not api.finded_com("xray") then
return
end
local type_name = "Xray"
+-- [[ Xray ]]
+
+s.fields["type"]:value(type_name, "Xray")
+if not s.fields["type"].default then
+ s.fields["type"].default = type_name
+end
+
+if s.val["type"] and s.val["type"] ~= type_name then
+ return
+end
+
local option_prefix = "xray_"
local function _n(name)
@@ -22,13 +31,6 @@ local header_type_list = {
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard", "dns"
}
--- [[ Xray ]]
-
-s.fields["type"]:value(type_name, "Xray")
-if not s.fields["type"].default then
- s.fields["type"].default = type_name
-end
-
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
@@ -487,8 +489,11 @@ o:depends({ [_n("outbound_node")] = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"})
o = s:option(Value, _n("outbound_node_iface"), translate("Interface"))
-o.default = "eth1"
o:depends({ [_n("outbound_node")] = "_iface"})
+local netdev_list = api.get_network_devices()
+for _, d in ipairs(netdev_list) do
+ o:value(d.name, d.label)
+end
o = s:option(TextValue, _n("custom_config"), translate("Custom Config"))
o.rows = 10
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 96beede8..dd3cb2f4 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
@@ -1,30 +1,13 @@
local m, s = ...
-local api = require "luci.passwall2.api"
-
local singbox_bin = api.finded_com("sing-box")
if not singbox_bin then
return
end
-local fs = api.fs
-
-local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
-
local type_name = "sing-box"
-local option_prefix = "singbox_"
-
-local function _n(name)
- return option_prefix .. name
-end
-
-local ss_method_list = {
- "none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305",
- "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
-}
-
-- [[ Sing-Box ]]
s.fields["type"]:value(type_name, "Sing-Box")
@@ -32,6 +15,23 @@ if not s.fields["type"].default then
s.fields["type"].default = type_name
end
+if s.val["type"] and s.val["type"] ~= type_name then
+ return
+end
+
+local option_prefix = "singbox_"
+
+local function _n(name)
+ return option_prefix .. name
+end
+
+local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
+
+local ss_method_list = {
+ "none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305",
+ "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
+}
+
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
@@ -58,7 +58,6 @@ o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
-o.rmempty = false
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("auth"), translate("Auth"))
@@ -302,7 +301,7 @@ o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o.validate = function(self, value, t)
if value and value ~= "" then
- if not nixio.fs.access(value) then
+ if not fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
@@ -321,7 +320,7 @@ o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o.validate = function(self, value, t)
if value and value ~= "" then
- if not nixio.fs.access(value) then
+ if not fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
@@ -464,8 +463,11 @@ o:depends({ [_n("outbound_node")] = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" })
o = s:option(Value, _n("outbound_node_iface"), translate("Interface"))
-o.default = "eth1"
o:depends({ [_n("outbound_node")] = "_iface" })
+local netdev_list = api.get_network_devices()
+for _, d in ipairs(netdev_list) do
+ o:value(d.name, d.label)
+end
o = s:option(TextValue, _n("custom_config"), translate("Custom Config"))
o.rows = 10
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua
index 2b800be2..d0939430 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua
@@ -1,13 +1,19 @@
local m, s = ...
-local api = require "luci.passwall2.api"
-
if not api.is_finded("ssserver") then
return
end
local type_name = "SS-Rust"
+-- [[ Shadowsocks Rust ]]
+
+s.fields["type"]:value(type_name, translate("Shadowsocks Rust"))
+
+if s.val["type"] and s.val["type"] ~= type_name then
+ return
+end
+
local option_prefix = "ssrust_"
local function _n(name)
@@ -20,10 +26,6 @@ local ssrust_encrypt_method_list = {
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305"
}
--- [[ Shadowsocks Rust ]]
-
-s.fields["type"]:value(type_name, translate("Shadowsocks Rust"))
-
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss.lua
deleted file mode 100644
index 093ab70c..00000000
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss.lua
+++ /dev/null
@@ -1,78 +0,0 @@
-local m, s = ...
-
-local api = require "luci.passwall2.api"
-
-if not api.is_finded("ss-server") then
- return
-end
-
-local type_name = "SS"
-
-local option_prefix = "ss_"
-
-local function _n(name)
- return option_prefix .. name
-end
-
-local ss_encrypt_method_list = {
- "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
- "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb",
- "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20",
- "chacha20-ietf", -- aead
- "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
- "xchacha20-ietf-poly1305"
-}
-
--- [[ Shadowsocks ]]
-
-s.fields["type"]:value(type_name, translate("Shadowsocks"))
-
-o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
-
-o = s:option(Value, _n("port"), translate("Listen Port"))
-o.datatype = "port"
-o:depends({ [_n("custom")] = false })
-
-o = s:option(Value, _n("password"), translate("Password"))
-o.password = true
-o:depends({ [_n("custom")] = false })
-
-o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
-for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end
-o:depends({ [_n("custom")] = false })
-
-o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
-o.datatype = "uinteger"
-o.default = 300
-o:depends({ [_n("custom")] = false })
-
-o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"))
-o.default = "0"
-o:depends({ [_n("custom")] = false })
-
-o = s:option(TextValue, _n("custom_config"), translate("Custom Config"))
-o.rows = 10
-o.wrap = "off"
-o:depends({ [_n("custom")] = true })
-o.validate = function(self, value, t)
- if value and api.jsonc.parse(value) then
- return value
- else
- return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
- end
-end
-o.custom_cfgvalue = function(self, section, value)
- local config_str = m:get(section, "config_str")
- if config_str then
- return api.base64Decode(config_str)
- end
-end
-o.custom_write = function(self, section, value)
- m:set(section, "config_str", api.base64Encode(value) or "")
-end
-
-o = s:option(Flag, _n("log"), translate("Log"))
-o.default = "1"
-o.rmempty = false
-
-api.luci_types(arg[1], m, s, type_name, option_prefix)
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua
index 3bcf5597..02a94f61 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua
@@ -1,13 +1,19 @@
local m, s = ...
-local api = require "luci.passwall2.api"
-
if not api.is_finded("ssr-server") then
return
end
local type_name = "SSR"
+-- [[ ShadowsocksR ]]
+
+s.fields["type"]:value(type_name, translate("ShadowsocksR"))
+
+if s.val["type"] and s.val["type"] ~= type_name then
+ return
+end
+
local option_prefix = "ssr_"
local function _n(name)
@@ -33,10 +39,6 @@ local ssr_obfs_list = {
"tls1.0_session_auth", "tls1.2_ticket_auth"
}
--- [[ ShadowsocksR ]]
-
-s.fields["type"]:value(type_name, translate("ShadowsocksR"))
-
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua
index a5dc64a9..c50d5ca9 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua
@@ -1,7 +1,6 @@
-local api = require "luci.passwall2.api"
-local appname = api.appname
-local fs = api.fs
-local types_dir = "/usr/lib/lua/luci/model/cbi/" .. appname .. "/server/type/"
+api = require "luci.passwall2.api"
+appname = api.appname
+fs = api.fs
m = Map("passwall2_server", translate("Server Config"))
m.redirect = api.url("server")
@@ -11,12 +10,21 @@ if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(m.redirect)
end
+local header = Template(appname .. "/server/config_header")
+header.api = api
+header.config = m.config
+header.section = arg[1]
+m:append(header)
+
m:append(Template(appname .. "/cbi/nodes_listvalue_com"))
s = m:section(NamedSection, arg[1], "user", "")
s.addremove = false
s.dynamic = false
+local types_dir = "/usr/lib/lua/luci/model/cbi/" .. appname .. "/server/type/"
+s.val = m:get(arg[1]) or {}
+
o = s:option(Flag, "enable", translate("Enable"))
o.default = "1"
o.rmempty = false
@@ -32,8 +40,6 @@ for filename in fs.dir(types_dir) do
table.insert(type_table, filename)
end
table.sort(type_table, function(a, b)
- if a == "socks.lua" then return true end
- if b == "socks.lua" then return false end
return a < b
end)
@@ -42,4 +48,10 @@ for index, value in ipairs(type_table) do
setfenv(p_func, getfenv(1))(m, s)
end
+local footer = Template(appname .. "/server/config_footer")
+footer.api = api
+footer.config = m.config
+footer.section = arg[1]
+m:append(footer)
+
return m
diff --git a/luci-app-passwall2/luasrc/passwall2/api.lua b/luci-app-passwall2/luasrc/passwall2/api.lua
index 71a59821..e2510f51 100644
--- a/luci-app-passwall2/luasrc/passwall2/api.lua
+++ b/luci-app-passwall2/luasrc/passwall2/api.lua
@@ -1367,6 +1367,11 @@ function set_apply_on_parse(map)
end
function luci_types(id, m, s, type_name, option_prefix)
+ local fv_type
+ local field_type = s.fields["type"]
+ if field_type then
+ fv_type = field_type:formvalue(id)
+ end
local rewrite_option_table = {}
for key, value in pairs(s.fields) do
if key:find(option_prefix) == 1 then
@@ -1435,6 +1440,10 @@ function luci_types(id, m, s, type_name, option_prefix)
else
s.fields[key]:depends({ type = type_name })
end
+
+ if fv_type and fv_type ~= type_name then
+ s.fields[key].rmempty = true
+ end
end
end
end
@@ -1623,3 +1632,83 @@ function parse_realm_uri(uri)
end
return realm
end
+
+function get_network_devices()
+ local _sysnet = "/sys/class/net/"
+ -- Map UCI interface names to their device names and vice versa
+ local _iface_to_dev = {}
+ local _dev_to_ifaces = {}
+ local _iface_proto = {}
+ uci:foreach("network", "interface", function(sec)
+ local name = sec[".name"]
+ if name ~= "loopback" then
+ _iface_proto[name] = sec.proto
+ if sec.device then
+ _iface_to_dev[name] = sec.device
+ _dev_to_ifaces[sec.device] = _dev_to_ifaces[sec.device] or {}
+ table.insert(_dev_to_ifaces[sec.device], name)
+ end
+ end
+ end)
+ -- Classify device type using sysfs attributes
+ local function classify_sysfs(dev)
+ if fs.stat(_sysnet .. dev .. "/bridge", "type") == "dir" then
+ return i18n.translate("Bridge")
+ elseif fs.stat(_sysnet .. dev .. "/wireless", "type") == "dir" then
+ return i18n.translate("Wireless Adapter")
+ elseif dev:match("^tun") or dev:match("^tap") or dev:match("^wg") or dev:match("^ppp") then
+ return i18n.translate("Tunnel Interface")
+ else
+ return i18n.translate("Ethernet Adapter")
+ end
+ end
+ -- Classify offline UCI interfaces by config hints
+ local function classify_uci(dev_name, proto)
+ if dev_name and dev_name:match("^br%-") then
+ return i18n.translate("Bridge")
+ elseif proto == "wireguard" or proto == "pppoe" or proto == "pptp" or proto == "l2tp" then
+ return i18n.translate("Tunnel Interface")
+ else
+ return i18n.translate("Interface")
+ end
+ end
+
+ local _seen = {}
+ local _devices = {}
+ -- Active kernel devices from /sys/class/net/
+ -- Skip bridge member ports (/master) and DSA master devices (/dsa)
+ local _iter = fs.dir(_sysnet)
+ if _iter then
+ for dev in _iter do
+ if dev ~= "lo"
+ and not dev:match("^veth")
+ and not dev:match("^ifb")
+ and not dev:match("^gre")
+ and not dev:match("^sit")
+ and not dev:match("^ip6tnl")
+ and not dev:match("^erspan")
+ and not fs.stat(_sysnet .. dev .. "/master", "type")
+ and not fs.stat(_sysnet .. dev .. "/dsa", "type")
+ then
+ local dtype = classify_sysfs(dev)
+ local label = dtype .. ': "' .. dev .. '"'
+ if _dev_to_ifaces[dev] then
+ label = label .. " (" .. table.concat(_dev_to_ifaces[dev], ", ") .. ")"
+ end
+ _devices[#_devices + 1] = { name = dev, label = label, sort = dtype .. ":" .. dev }
+ _seen[dev] = true
+ end
+ end
+ end
+ -- UCI interfaces whose device does not currently exist
+ for iface, dev in pairs(_iface_to_dev) do
+ if not _seen[dev] then
+ local dtype = classify_uci(dev, _iface_proto[iface])
+ local label = dtype .. ': "' .. iface .. '"'
+ _devices[#_devices + 1] = { name = iface, label = label, sort = "zzz:" .. iface }
+ _seen[dev] = true
+ end
+ end
+ table.sort(_devices, function(a, b) return a.sort < b.sort end)
+ return _devices
+end
diff --git a/luci-app-passwall2/luasrc/passwall2/server_app.lua b/luci-app-passwall2/luasrc/passwall2/server_app.lua
index 6203614f..360305ff 100644
--- a/luci-app-passwall2/luasrc/passwall2/server_app.lua
+++ b/luci-app-passwall2/luasrc/passwall2/server_app.lua
@@ -120,7 +120,7 @@ local function start()
local config_file = CONFIG_PATH .. "/" .. id .. ".json"
local udp_forward = 1
local type = user.type or ""
- if type == "SS" or type == "SSR" then
+ if type == "SSR" then
if user.custom == "1" and user.config_str then
config = jsonc.parse(api.base64Decode(user.config_str))
else
diff --git a/luci-app-passwall2/luasrc/passwall2/util_shadowsocks.lua b/luci-app-passwall2/luasrc/passwall2/util_shadowsocks.lua
index 5978991b..f4645138 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_shadowsocks.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_shadowsocks.lua
@@ -75,11 +75,7 @@ function gen_config(var)
reuse_port = true
}
- if node.type == "SS" then
- config.plugin = plugin_file or nil
- config.plugin_opts = (plugin_file) and node.plugin_opts or nil
- config.mode = mode
- elseif node.type == "SSR" then
+ if node.type == "SSR" then
config.protocol = node.protocol
config.protocol_param = node.protocol_param
config.obfs = node.obfs
diff --git a/luci-app-passwall2/luasrc/passwall2/util_tuic.lua b/luci-app-passwall2/luasrc/passwall2/util_tuic.lua
deleted file mode 100644
index 9695c4f4..00000000
--- a/luci-app-passwall2/luasrc/passwall2/util_tuic.lua
+++ /dev/null
@@ -1,68 +0,0 @@
-module("luci.passwall2.util_tuic", package.seeall)
-local api = require "luci.passwall2.api"
-local uci = api.uci
-local jsonc = api.jsonc
-
-function gen_config(var)
- local node_id = var["node"]
- if not node_id then
- print("node Cannot be empty!")
- return
- end
- local node = uci:get_all("passwall2", node_id)
- local local_addr = var["local_addr"]
- local local_port = var["local_port"]
- local server_host = var["server_host"] or (node.address or ""):lower()
- local server_port = var["server_port"] or node.port
- local loglevel = var["loglevel"] or "warn"
-
- local tuic= {
- relay = {
- server = server_host .. ":" .. server_port,
- ip = node.tuic_ip,
- uuid = node.uuid,
- password = node.tuic_password,
- -- certificates = node.tuic_certificate and { node.tuic_certpath } or nil,
- udp_relay_mode = node.tuic_udp_relay_mode,
- congestion_control = node.tuic_congestion_control,
- heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
- timeout = (tonumber(node.tuic_timeout) or 8) .. "s",
- gc_interval = (tonumber(node.tuic_gc_interval) or 3) .. "s",
- gc_lifetime = (tonumber(node.tuic_gc_lifetime) or 15) .. "s",
- alpn = (node.tuic_tls_alpn and node.tuic_tls_alpn ~= "") and (function()
- local alpn = {}
- string.gsub(node.tuic_tls_alpn, '[^,]+', function(w)
- table.insert(alpn, w)
- end)
- if #alpn > 0 then return alpn end
- return nil
- end)() or nil,
- disable_sni = (node.tuic_disable_sni == "1"),
- zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1"),
- send_window = tonumber(node.tuic_send_window),
- receive_window = tonumber(node.tuic_receive_window)
- },
- ["local"] = {
- server = "[::]:" .. local_port,
- username = node.tuic_socks_username,
- password = node.tuic_socks_password,
- dual_stack = (node.tuic_dual_stack == "1") and true or false,
- max_packet_size = tonumber(node.tuic_max_package_size)
- },
- log_level = loglevel
- }
- return jsonc.stringify(tuic, 1)
-end
-
-_G.gen_config = gen_config
-
-if arg[1] then
- local func =_G[arg[1]]
- if func then
- local var = nil
- if arg[2] then
- var = jsonc.parse(arg[2])
- end
- print(func(var))
- end
-end
diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
index cf9fd5f6..b674e971 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
@@ -1219,6 +1219,11 @@ function gen_config(var)
end
end
end
+ if node.chain_proxy == "3" and node.outbound_iface then
+ if outbound.streamSettings and outbound.streamSettings.sockopt then
+ outbound.streamSettings.sockopt.interface = node.outbound_iface
+ end
+ end
return default_outTag, last_insert_outbound
end
diff --git a/luci-app-passwall2/luasrc/view/passwall2/node_config/footer.htm b/luci-app-passwall2/luasrc/view/passwall2/node_config/footer.htm
index 2d9e3c77..feefc4dd 100644
--- a/luci-app-passwall2/luasrc/view/passwall2/node_config/footer.htm
+++ b/luci-app-passwall2/luasrc/view/passwall2/node_config/footer.htm
@@ -4,7 +4,7 @@ local api = self.api
diff --git a/luci-app-passwall2/luasrc/view/passwall2/server/config_header.htm b/luci-app-passwall2/luasrc/view/passwall2/server/config_header.htm
new file mode 100644
index 00000000..1957dec4
--- /dev/null
+++ b/luci-app-passwall2/luasrc/view/passwall2/server/config_header.htm
@@ -0,0 +1,56 @@
+<%
+local api = self.api
+-%>
+
diff --git a/luci-app-passwall2/po/fa/passwall2.po b/luci-app-passwall2/po/fa/passwall2.po
index bd98dfe7..dfb60669 100644
--- a/luci-app-passwall2/po/fa/passwall2.po
+++ b/luci-app-passwall2/po/fa/passwall2.po
@@ -564,9 +564,6 @@ msgstr "اصلی"
msgid "Transport Plugin"
msgstr "افزونه انتقال"
-msgid "Shadowsocks secondary encryption"
-msgstr "رمزنگاری ثانویه Shadowsocks"
-
msgid "Obfs Type"
msgstr "نوع مبهمسازی (Obfs)"
@@ -1365,18 +1362,6 @@ msgstr "زمانی استفاده میشود که لینک گره شامل ا
msgid "%s Node Use Type"
msgstr "نوع استفاده گره %s"
-msgid "Set the TUIC proxy server ip address"
-msgstr "تنظیم آدرس IP سرور پروکسی TUIC"
-
-msgid "TUIC User Password For Connect Remote Server"
-msgstr "رمز عبور کاربر TUIC برای اتصال به سرور راه دور"
-
-msgid "TUIC UserName For Local Socks"
-msgstr "نام کاربری TUIC برای Socks محلی"
-
-msgid "TUIC Password For Local Socks"
-msgstr "رمز عبور TUIC برای Socks محلی"
-
msgid "UDP relay mode"
msgstr "حالت بازپخش UDP"
@@ -1389,33 +1374,12 @@ msgstr "الگوریتم کنترل ازدحام"
msgid "Heartbeat interval(second)"
msgstr "بازه ضربان قلب (ثانیه)"
-msgid "Timeout for establishing a connection to server(second)"
-msgstr "مهلت زمانی برای برقراری اتصال به سرور (ثانیه)"
-
-msgid "Garbage collection interval(second)"
-msgstr "بازه جمعآوری زباله (ثانیه)"
-
-msgid "Garbage collection lifetime(second)"
-msgstr "طول عمر جمعآوری زباله (ثانیه)"
-
msgid "Disable SNI"
msgstr "غیرفعال کردن SNI"
msgid "Enable 0-RTT QUIC handshake"
msgstr "فعالسازی دست تکان دادن 0-RTT QUIC"
-msgid "TUIC send window"
-msgstr "پنجره ارسال TUIC"
-
-msgid "TUIC receive window"
-msgstr "پنجره دریافت TUIC"
-
-msgid "TUIC Maximum packet size the socks5 server can receive from external, in bytes"
-msgstr "حداکثر اندازه بسته TUIC که سرور socks5 میتواند از خارج دریافت کند، به بایت"
-
-msgid "Set if the listening socket should be dual-stack"
-msgstr "تنظیم کنید که سوکت شنونده دو پشتهای (dual-stack) باشد یا خیر"
-
msgid " none: default, no masquerade, data sent is packets with no characteristics. srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime). utp: packets disguised as uTP will be recognized as bittorrent downloaded data. wechat-video: packets disguised as WeChat video calls. dtls: disguised as DTLS 1.2 packet. wireguard: disguised as a WireGuard packet. (not really WireGuard protocol) dns: Disguising traffic as DNS requests."
msgstr " none: پیشفرض، بدون استتار، دادههای ارسالی بستههای بدون مشخصه هستند. srtp: مبدل شده به بسته SRTP، به عنوان داده تماس تصویری (مانند FaceTime) شناخته میشود. utp: بستههای مبدل شده به uTP به عنوان داده دانلود شده bittorrent شناخته میشوند. wechat-video: بستههای مبدل شده به تماسهای تصویری WeChat. dtls: مبدل شده به بسته DTLS 1.2. wireguard: مبدل شده به بسته WireGuard. (نه واقعاً پروتکل WireGuard) dns: پنهان کردن ترافیک به عنوان درخواستهای DNS."
@@ -2323,9 +2287,6 @@ msgstr "Xray از افزونه %s پشتیبانی نمیکند."
msgid "Plugin options Incomplete."
msgstr "گزینههای افزونه ناقص است."
-msgid "shadowsocks-libev unsupport 2022 encryption."
-msgstr "shadowsocks-libev از رمزنگاری 2022 پشتیبانی نمیکند."
-
msgid "Please replace %s to support %s transmission method."
msgstr "لطفا برای پشتیبانی از روش انتقال %s، %s را جایگزین کنید."
diff --git a/luci-app-passwall2/po/ru/passwall2.po b/luci-app-passwall2/po/ru/passwall2.po
index 3e814ff5..cb5b8969 100644
--- a/luci-app-passwall2/po/ru/passwall2.po
+++ b/luci-app-passwall2/po/ru/passwall2.po
@@ -559,9 +559,6 @@ msgstr "Оригинал"
msgid "Transport Plugin"
msgstr "Плагин транспорта"
-msgid "Shadowsocks secondary encryption"
-msgstr "Дополнительное шифрование Shadowsocks"
-
msgid "global_padding"
msgstr "Включить padding-заполнение"
@@ -1366,18 +1363,6 @@ msgstr "Используется, если в ссылке на узел это
msgid "%s Node Use Type"
msgstr "Тип использования сервера %s"
-msgid "Set the TUIC proxy server ip address"
-msgstr "Указать IP-адрес сервера TUIC"
-
-msgid "TUIC User Password For Connect Remote Server"
-msgstr "Пароль для подключения к удалённому серверу TUIC"
-
-msgid "TUIC UserName For Local Socks"
-msgstr "Имя пользователя для локального SOCKS TUIC"
-
-msgid "TUIC Password For Local Socks"
-msgstr "Пароль для локального SOCKS TUIC"
-
msgid "UDP relay mode"
msgstr "Режим ретрансляции UDP"
@@ -1390,33 +1375,12 @@ msgstr "Алгоритм контроля перегрузок"
msgid "Heartbeat interval(second)"
msgstr "Интервал heartbeat-пакетов (сек)"
-msgid "Timeout for establishing a connection to server(second)"
-msgstr "Тайм-аут установки соединения с сервером (сек)"
-
-msgid "Garbage collection interval(second)"
-msgstr "Интервал очистки фрагментов UDP (сек)"
-
-msgid "Garbage collection lifetime(second)"
-msgstr "Время жизни фрагментов UDP на сервере (сек)"
-
msgid "Disable SNI"
msgstr "Отключить SNI"
msgid "Enable 0-RTT QUIC handshake"
msgstr "Включить 0-RTT рукопожатие QUIC"
-msgid "TUIC send window"
-msgstr "Окно отправки TUIC (макс. байт без подтверждения: по умолчанию 8 МБ × 2)"
-
-msgid "TUIC receive window"
-msgstr "Окно приёма TUIC (макс. байт без подтверждения: по умолчанию 8 МБ)"
-
-msgid "TUIC Maximum packet size the socks5 server can receive from external, in bytes"
-msgstr "Макс. размер пакета, который SOCKS5-сервер TUIC может принять извне (в байтах)"
-
-msgid "Set if the listening socket should be dual-stack"
-msgstr "Включить поддержку dual-stack для слушающего сокета"
-
msgid " none: default, no masquerade, data sent is packets with no characteristics. srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime). utp: packets disguised as uTP will be recognized as bittorrent downloaded data. wechat-video: packets disguised as WeChat video calls. dtls: disguised as DTLS 1.2 packet. wireguard: disguised as a WireGuard packet. (not really WireGuard protocol) dns: Disguising traffic as DNS requests."
msgstr " none: по умолчанию, без маскировки, данные отправляются обычными пакетами. srtp: маскировка под SRTP — трафик определяется как видеозвонок (например, FaceTime). utp: маскировка под uTP — трафик определяется как BitTorrent. wechat-video: маскировка под видеозвонок WeChat. dtls: маскировка под DTLS 1.2. wireguard: маскировка под WireGuard (не настоящий протокол WireGuard). dns: маскировка трафика под DNS-запросы."
@@ -2321,9 +2285,6 @@ msgstr "Xray не поддерживает плагин %s."
msgid "Plugin options Incomplete."
msgstr "Опции плагина указаны не полностью."
-msgid "shadowsocks-libev unsupport 2022 encryption."
-msgstr "shadowsocks-libev не поддерживает шифрование 2022."
-
msgid "Please replace %s to support %s transmission method."
msgstr "Пожалуйста, замените %s, чтобы поддержать метод передачи %s."
diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po
index 36492fcb..1010101b 100644
--- a/luci-app-passwall2/po/zh-cn/passwall2.po
+++ b/luci-app-passwall2/po/zh-cn/passwall2.po
@@ -556,9 +556,6 @@ msgstr "原版"
msgid "Transport Plugin"
msgstr "传输层插件"
-msgid "Shadowsocks secondary encryption"
-msgstr "Shadowsocks 二次加密"
-
msgid "Obfs Type"
msgstr "混淆类型"
@@ -1357,18 +1354,6 @@ msgstr "当节点链接未包含该参数时,将使用此设置。"
msgid "%s Node Use Type"
msgstr "%s 节点使用类型"
-msgid "Set the TUIC proxy server ip address"
-msgstr "指定远程TUIC服务器IP"
-
-msgid "TUIC User Password For Connect Remote Server"
-msgstr "用于远程TUIC服务器连接的密码"
-
-msgid "TUIC UserName For Local Socks"
-msgstr "用于本地Socks服务器连接的用户名"
-
-msgid "TUIC Password For Local Socks"
-msgstr "用于本地Socks服务器连接的密码"
-
msgid "UDP relay mode"
msgstr "UDP中继模式"
@@ -1381,33 +1366,12 @@ msgstr "拥塞控制算法"
msgid "Heartbeat interval(second)"
msgstr "保活心跳包发送间隔(单位:秒)"
-msgid "Timeout for establishing a connection to server(second)"
-msgstr "连接超时时间(单位:秒)"
-
-msgid "Garbage collection interval(second)"
-msgstr "UDP数据包片残片清理间隔(单位:秒)"
-
-msgid "Garbage collection lifetime(second)"
-msgstr "UDP数据包残片在服务器的保留时间(单位:秒)"
-
msgid "Disable SNI"
msgstr "关闭SNI服务器名称指示"
msgid "Enable 0-RTT QUIC handshake"
msgstr "客户端启用 0-RTT QUIC 连接握手"
-msgid "TUIC send window"
-msgstr "发送窗口(无需确认即可发送的最大字节数:默认8Mb*2)"
-
-msgid "TUIC receive window"
-msgstr "接收窗口(无需确认即可接收的最大字节数:默认8Mb)"
-
-msgid "TUIC Maximum packet size the socks5 server can receive from external, in bytes"
-msgstr "TUIC socks5 服务器可以从外部接收的最大数据包大小(以字节为单位)"
-
-msgid "Set if the listening socket should be dual-stack"
-msgstr "设置监听套接字为双栈"
-
msgid " none: default, no masquerade, data sent is packets with no characteristics. srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime). utp: packets disguised as uTP will be recognized as bittorrent downloaded data. wechat-video: packets disguised as WeChat video calls. dtls: disguised as DTLS 1.2 packet. wireguard: disguised as a WireGuard packet. (not really WireGuard protocol) dns: Disguising traffic as DNS requests."
msgstr " none:默认值,不进行伪装,发送的数据是没有特征的数据包。 srtp:伪装成 SRTP 数据包,会被识别为视频通话数据(如 FaceTime)。 utp:伪装成 uTP 数据包,会被识别为 BT 下载数据。 wechat-video:伪装成微信视频通话的数据包。 dtls:伪装成 DTLS 1.2 数据包。 wireguard:伪装成 WireGuard 数据包。(并不是真正的 WireGuard 协议) dns:把流量伪装成 DNS 请求。"
@@ -2309,9 +2273,6 @@ msgstr "Xray 不支持 %s 插件。"
msgid "Plugin options Incomplete."
msgstr "插件选项不完整。"
-msgid "shadowsocks-libev unsupport 2022 encryption."
-msgstr "shadowsocks-libev 不支持2022加密。"
-
msgid "Please replace %s to support %s transmission method."
msgstr "请更换 %s 以支持 %s 传输方式。"
diff --git a/luci-app-passwall2/po/zh-tw/passwall2.po b/luci-app-passwall2/po/zh-tw/passwall2.po
index ab40100e..efea133a 100644
--- a/luci-app-passwall2/po/zh-tw/passwall2.po
+++ b/luci-app-passwall2/po/zh-tw/passwall2.po
@@ -556,9 +556,6 @@ msgstr "原版"
msgid "Transport Plugin"
msgstr "傳輸層插件"
-msgid "Shadowsocks secondary encryption"
-msgstr "Shadowsocks 二次加密"
-
msgid "Obfs Type"
msgstr "混淆類型"
@@ -1357,18 +1354,6 @@ msgstr "當節點連結未包含該參數時,將使用此設定。"
msgid "%s Node Use Type"
msgstr "%s 節點使用類型"
-msgid "Set the TUIC proxy server ip address"
-msgstr "指定遠程TUIC服務器IP"
-
-msgid "TUIC User Password For Connect Remote Server"
-msgstr "用於遠程TUIC服務器連接的密碼"
-
-msgid "TUIC UserName For Local Socks"
-msgstr "用於本地Socks服務器連接的用戶名"
-
-msgid "TUIC Password For Local Socks"
-msgstr "用於本地Socks服務器連接的密碼"
-
msgid "UDP relay mode"
msgstr "UDP中继模式"
@@ -1381,33 +1366,12 @@ msgstr "拥塞控制算法"
msgid "Heartbeat interval(second)"
msgstr "保活心跳包發送間隔(單位:秒)"
-msgid "Timeout for establishing a connection to server(second)"
-msgstr "連接超時時間(單位:秒)"
-
-msgid "Garbage collection interval(second)"
-msgstr "UDP數据包片残片清理間隔(單位:秒)"
-
-msgid "Garbage collection lifetime(second)"
-msgstr "UDP數据包残片在服務器的保留時間(單位:秒)"
-
msgid "Disable SNI"
msgstr "關閉SNI服務器名称指示"
msgid "Enable 0-RTT QUIC handshake"
msgstr "客戶端啟用 0-RTT QUIC 連接握手"
-msgid "TUIC send window"
-msgstr "發送窗口(無需確認即可發送的最大字节數:默認8Mb*2)"
-
-msgid "TUIC receive window"
-msgstr "接收窗口(無需確認即可接收的最大字节數:默認8Mb)"
-
-msgid "TUIC Maximum packet size the socks5 server can receive from external, in bytes"
-msgstr "TUIC socks5 服務器可以从外部接收的最大數据包大小(以字节為單位)"
-
-msgid "Set if the listening socket should be dual-stack"
-msgstr "設置監聽套接字為双栈"
-
msgid " none: default, no masquerade, data sent is packets with no characteristics. srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime). utp: packets disguised as uTP will be recognized as bittorrent downloaded data. wechat-video: packets disguised as WeChat video calls. dtls: disguised as DTLS 1.2 packet. wireguard: disguised as a WireGuard packet. (not really WireGuard protocol) dns: Disguising traffic as DNS requests."
msgstr " none:默認值,不进行伪装,發送的數据是沒有特征的數据包。 srtp:伪装成 SRTP 數据包,会被识别為視频通话數据(如 FaceTime)。 utp:伪装成 uTP 數据包,会被识别為 BT 下載數据。 wechat-video:伪装成微信視频通话的數据包。 dtls:伪装成 DTLS 1.2 數据包。 wireguard:伪装成 WireGuard 數据包。(並不是真正的 WireGuard 協議) dns:把流量伪装成 DNS 請求。"
@@ -2315,9 +2279,6 @@ msgstr "Xray 不支持 %s 插件。"
msgid "Plugin options Incomplete."
msgstr "插件選項不完整。"
-msgid "shadowsocks-libev unsupport 2022 encryption."
-msgstr "shadowsocks-libev 不支持2022加密。"
-
msgid "Please replace %s to support %s transmission method."
msgstr "請更換 %s 以支援 %s 傳輸方式。"
diff --git a/luci-app-passwall2/root/usr/share/passwall2/app.sh b/luci-app-passwall2/root/usr/share/passwall2/app.sh
index 75dff526..b65a3217 100755
--- a/luci-app-passwall2/root/usr/share/passwall2/app.sh
+++ b/luci-app-passwall2/root/usr/share/passwall2/app.sh
@@ -14,7 +14,6 @@ UTIL_SS=$LUA_UTIL_PATH/util_shadowsocks.lua
UTIL_XRAY=$LUA_UTIL_PATH/util_xray.lua
UTIL_NAIVE=$LUA_UTIL_PATH/util_naiveproxy.lua
UTIL_HYSTERIA2=$LUA_UTIL_PATH/util_hysteria2.lua
-UTIL_TUIC=$LUA_UTIL_PATH/util_tuic.lua
SINGBOX_BIN=$(first_type $(config_t_get global_app sing_box_file) sing-box)
XRAY_BIN=$(first_type $(config_t_get global_app xray_file) xray)
@@ -467,18 +466,6 @@ run_socks() {
lua $UTIL_SS gen_config "${_json_arg}" > $config_file
[ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type ssr-local)" "ssr-local" $log_file -c "$config_file" -v -u
;;
- ss)
- json_add_string "local_addr" "${bind}"
- json_add_string "local_port" "${socks_port}"
- json_add_string "mode" "tcp_and_udp"
- [ -z "$no_run" ] && {
- local plugin_sh="${config_file%.json}_plugin.sh"
- json_add_string "plugin_sh" "${plugin_sh}"
- }
- local _json_arg="$(json_dump)"
- lua $UTIL_SS gen_config "${_json_arg}" > $config_file
- [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type ss-local)" "ss-local" $log_file -c "$config_file" -v
- ;;
ss-rust)
json_add_string "local_socks_address" "${bind}"
json_add_string "local_socks_port" "${socks_port}"
@@ -509,13 +496,6 @@ run_socks() {
lua $UTIL_HYSTERIA2 gen_config "${_json_arg}" > $config_file
[ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type $(config_t_get global_app hysteria_file))" "hysteria" $log_file -c "$config_file" client
;;
- tuic)
- json_add_string "local_addr" "${bind}"
- json_add_string "local_port" "${socks_port}"
- local _json_arg="$(json_dump)"
- lua $UTIL_TUIC gen_config "${_json_arg}" > $config_file
- [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type tuic-client)" "tuic-client" $log_file -c "$config_file"
- ;;
esac
# http to socks
diff --git a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua
index c374fb43..35104922 100755
--- a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua
+++ b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua
@@ -25,7 +25,6 @@ local log = api.log
local i18n = api.i18n
local lyaml = require "lyaml"
-local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal")
local has_ssr = api.is_finded("ssr-local") and api.is_finded("ssr-redir")
local has_singbox = api.finded_com("sing-box")
@@ -36,7 +35,7 @@ local DEFAULT_FILTER_KEYWORD_MODE = uci:get(appname, "@global_subscribe[0]", "fi
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 {}
-- Nodes should be retrieved using the core type (if not set on the node subscription page, the default type will be used automatically).
-local DEFAULT_SS_TYPE = api.get_core("ss_type", {{has_ss,"shadowsocks-libev"},{has_ss_rust,"shadowsocks-rust"},{has_singbox,"sing-box"},{has_xray,"xray"}})
+local DEFAULT_SS_TYPE = api.get_core("ss_type", {{has_ss_rust,"shadowsocks-rust"},{has_singbox,"sing-box"},{has_xray,"xray"}})
local DEFAULT_TROJAN_TYPE = api.get_core("trojan_type", {{has_singbox,"sing-box"},{has_xray,"xray"}})
local DEFAULT_VMESS_TYPE = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local DEFAULT_VLESS_TYPE = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
@@ -44,7 +43,6 @@ local DEFAULT_HYSTERIA2_TYPE = api.get_core("hysteria2_type", {{has_hysteria2,"h
local core_has = {
["xray"] = has_xray,
["sing-box"] = has_singbox,
- ["shadowsocks-libev"] = has_ss,
["shadowsocks-rust"] = has_ss_rust,
["hysteria2"] = has_hysteria2
}
@@ -460,9 +458,7 @@ end
-- Configure the SS protocol implementation type
local function set_ss_implementation(ss_type, result)
- if ss_type == "shadowsocks-libev" and has_ss then
- result.type = "SS"
- elseif ss_type == "shadowsocks-rust" and has_ss_rust then
+ if ss_type == "shadowsocks-rust" and has_ss_rust then
result.type = 'SS-Rust'
elseif ss_type == "xray" and has_xray then
result.type = 'Xray'
@@ -485,6 +481,7 @@ local function parseClashNode(node, add_mode, group, sub_cfg)
local sub_vmess_type = DEFAULT_VMESS_TYPE
local sub_vless_type = DEFAULT_VLESS_TYPE
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
@@ -509,6 +506,8 @@ local function parseClashNode(node, add_mode, group, sub_cfg)
if hysteria2_type ~= "global" and core_has[hysteria2_type] then
sub_hysteria2_type = hysteria2_type
end
+ sub_hy_up_mbps = sub_cfg.hysteria_up_mbps
+ sub_hy_down_mbps = sub_cfg.hysteria_down_mbps
end
local result = {
timeout = 60,
@@ -778,6 +777,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
local sub_vmess_type = DEFAULT_VMESS_TYPE
local sub_vless_type = DEFAULT_VLESS_TYPE
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
@@ -802,6 +802,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if hysteria2_type ~= "global" and core_has[hysteria2_type] then
sub_hysteria2_type = hysteria2_type
end
+ sub_hy_up_mbps = sub_cfg.hysteria_up_mbps
+ sub_hy_down_mbps = sub_cfg.hysteria_down_mbps
end
local result = {
timeout = 60,
@@ -1113,20 +1115,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end
end
- if result.type == "SS" then
- local aead2022_methods = { "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" }
- local aead2022 = false
- for k, v in ipairs(aead2022_methods) do
- if method:lower() == v:lower() then
- aead2022 = true
- end
- end
- if aead2022 then
- -- shadowsocks-libev does not support 2022 encryption.
- result.error_msg = i18n.translatef("shadowsocks-libev unsupport 2022 encryption.")
- end
- end
-
if params.type then
params.type = string.lower(params.type)
if result.type == "sing-box" and params.type == "raw" then
@@ -1140,7 +1128,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
else
result.transport = params.type
end
- if result.type ~= "SS-Rust" and result.type ~= "SS" then
+ if result.type ~= "SS-Rust" then
if params.type == 'ws' then
result.ws_host = params.host
result.ws_path = params.path
@@ -1647,8 +1635,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
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
- result.hysteria_down_mbps = params.downmbps
+ result.hysteria_up_mbps = params.upmbps or sub_hy_up_mbps
+ result.hysteria_down_mbps = params.downmbps or sub_hy_down_mbps
result.hysteria_hop = params.mport
elseif szType == 'hysteria2' or szType == 'hy2' then
@@ -1692,6 +1680,8 @@ local function processData(szType, content, add_mode, group, sub_cfg)
result.tls_CertByName = params.vcn
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_hy_up_mbps
+ result.hysteria2_down_mbps = params.downmbps or sub_hy_down_mbps
result.hysteria2_hop = params.mport
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs_type = "salamander"
@@ -2180,7 +2170,7 @@ local function update_node(manual)
local list = v["list"]
local sub_cfg = v["sub_cfg"]
local domain_resolver, domain_resolver_dns, domain_resolver_dns_https, domain_strategy
- local preproxy_node_group, to_node_group, chain_node_type = "", "", ""
+ local preproxy_node_group, to_node_group, outbound_iface_group, chain_node_type = "", "", "", ""
-- Subscription Group Chain Agent
local function valid_chain_node(node)
if not node then return "" end
@@ -2200,6 +2190,8 @@ local function update_node(manual)
domain_strategy = (sub_cfg.domain_strategy == "UseIPv4" or sub_cfg.domain_strategy == "UseIPv6") and sub_cfg.domain_strategy or nil
preproxy_node_group = (sub_cfg.chain_proxy == "1") and valid_chain_node(sub_cfg.preproxy_node) or ""
to_node_group = (sub_cfg.chain_proxy == "2") and valid_chain_node(sub_cfg.to_node) or ""
+ outbound_iface_group = (sub_cfg.chain_proxy == "3") and sub_cfg.outbound_iface or ""
+ chain_node_type = (outbound_iface_group ~= "") and "iface" or chain_node_type
end
for _, vv in ipairs(list) do
local cfgid = uci:section(appname, "nodes", api.gen_short_uuid())
@@ -2235,6 +2227,9 @@ local function update_node(manual)
elseif to_node_group ~= "" then
uci:set(appname, cfgid, "chain_proxy", "2")
uci:set(appname, cfgid, "to_node", to_node_group)
+ elseif outbound_iface_group ~= "" then
+ uci:set(appname, cfgid, "chain_proxy", "3")
+ uci:set(appname, cfgid, "outbound_iface", outbound_iface_group)
end
end
end
diff --git a/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua b/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua
index 014fe3af..79430d37 100644
--- a/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua
+++ b/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua
@@ -647,6 +647,8 @@ function index()
entry({"admin", "services", "shadowsocksr", "ping"}, call("act_ping"))
entry({"admin", "services", "shadowsocksr", "save_order"}, call("save_order")).leaf = true
entry({"admin", "services", "shadowsocksr", "delete_node"}, call("act_delete_node")).leaf = true
+ entry({"admin", "services", "shadowsocksr", "add_subscribe_item"}, call("add_subscribe_item")).leaf = true
+ entry({"admin", "services", "shadowsocksr", "delete_subscribe_item"}, call("delete_subscribe_item")).leaf = true
entry({"admin", "services", "shadowsocksr", "toggle_subscribe_item_enabled"}, call("toggle_subscribe_item_enabled")).leaf = true
entry({"admin", "services", "shadowsocksr", "reset"}, call("act_reset"))
entry({"admin", "services", "shadowsocksr", "restart"}, call("act_restart"))
@@ -665,7 +667,9 @@ end
function subscribe()
nixio.fs.remove(SERVER_DETECT_CACHE)
- local ret = luci.sys.call(": > /var/log/ssrplus.log && /usr/bin/lua /usr/share/shadowsocksr/subscribe.lua >>/var/log/ssrplus.log 2>&1")
+ local sid = luci.http.formvalue("sid") or ""
+ local subscribe_arg = sid ~= "" and (" " .. luci.util.shellquote(sid)) or ""
+ local ret = luci.sys.call(": > /var/log/ssrplus.log && /usr/bin/lua /usr/share/shadowsocksr/subscribe.lua" .. subscribe_arg .. " >>/var/log/ssrplus.log 2>&1")
luci.http.prepare_content("application/json")
luci.http.write_json({ret = ret})
end
@@ -775,8 +779,67 @@ function act_delete_node()
return
end
- local com_cmd = luci.sys.call("uci -q commit shadowsocksr >/dev/null 2>&1")
- if com_cmd ~= 0 then
+ local ret_cmd = luci.sys.call("uci -q commit shadowsocksr >/dev/null 2>&1")
+ if ret_cmd ~= 0 then
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "commit failed" })
+ return
+ end
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 1, sid = sid })
+end
+
+function add_subscribe_item()
+ local sid = luci.sys.exec("uci add shadowsocksr server_subscribe_item"):gsub("%s+", "")
+
+ if not sid or sid == "" then
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "add failed" })
+ return
+ end
+
+ local alias = string.format("Subscribe %s", sid:sub(-4))
+
+ -- set enabled
+ local subscribe_enabled = luci.sys.call("uci -q set shadowsocksr." .. sid .. ".enabled=1")
+ if subscribe_enabled ~= 0 then
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "set enabled failed" })
+ return
+ end
+
+ -- set alias
+ local subscribe_alias = luci.sys.call("uci -q set shadowsocksr." .. sid .. ".alias='" .. alias .. "'")
+ if subscribe_alias ~= 0 then
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "set alias failed" })
+ return
+ end
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 1, sid = sid, alias = alias, enabled = "1" })
+end
+
+function delete_subscribe_item()
+ local sid = trim(luci.http.formvalue("sid"))
+ if sid == "" then
+ luci.http.status(400, "Bad Request")
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "missing sid" })
+ return
+ end
+
+ local delete_subscribe_set = luci.sys.call("uci -q delete shadowsocksr." .. sid .. " 2>/dev/null")
+ if delete_subscribe_set ~= 0 then
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "delete failed" })
+ return
+ end
+
+ -- commit
+ local delete_subscribe_cmd = luci.sys.call("uci -q commit shadowsocksr >/dev/null 2>&1")
+ if delete_subscribe_cmd ~= 0 then
luci.http.prepare_content("application/json")
luci.http.write_json({ ret = 0, error = "commit failed" })
return
@@ -788,21 +851,69 @@ end
function toggle_subscribe_item_enabled()
local sid = trim(luci.http.formvalue("sid"))
- local enabled = luci.http.formvalue("enabled") == "1" and "1" or "0"
+ local field = luci.http.formvalue("field")
+ local value = luci.http.formvalue("value")
- if sid == "" or uci:get("shadowsocksr", sid) ~= "server_subscribe_item" then
+ -- 兼容旧调用方式(只传 enabled)
+ if not field then
+ field = "enabled"
+ value = luci.http.formvalue("enabled") == "1" and "1" or "0"
+ end
+
+ -- 参数校验
+ if sid == "" then
+ luci.http.status(400, "Bad Request")
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "missing sid" })
+ return
+ end
+
+ -- 检查 sid 对应的 section 类型
+ if uci:get("shadowsocksr", sid) ~= "server_subscribe_item" then
luci.http.status(400, "Bad Request")
luci.http.prepare_content("application/json")
luci.http.write_json({ ret = 0, error = "invalid_sid" })
return
end
- uci:set("shadowsocksr", sid, "enabled", enabled)
- uci:save("shadowsocksr")
- uci:commit("shadowsocksr")
+ -- 白名单:只允许以下字段
+ local allowed_fields = { enabled = true, alias = true, url = true }
+ if not allowed_fields[field] then
+ luci.http.status(400, "Bad Request")
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "unsupported field" })
+ return
+ end
+
+ -- 处理字段值
+ if field == "enabled" then
+ value = (value == "1" or value == "true") and "1" or "0"
+ elseif field == "alias" or field == "url" then
+ value = value and trim(value) or ""
+ end
+
+ -- 转义 value 中的单引号(避免破坏 uci 命令)
+ local escaped_value = value:gsub("'", "'\\''")
+
+ -- 使用外部 uci 命令设置值
+ local set_cmd = string.format("uci -q set shadowsocksr.%s.%s='%s'", sid, field, escaped_value)
+ local set_ret = luci.sys.call(set_cmd .. " >/dev/null 2>&1")
+ if set_ret ~= 0 then
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "uci set failed" })
+ return
+ end
+
+ -- 提交更改
+ local ret_cmd = luci.sys.call("uci -q commit shadowsocksr >/dev/null 2>&1")
+ if ret_cmd ~= 0 then
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({ ret = 0, error = "commit failed" })
+ return
+ end
luci.http.prepare_content("application/json")
- luci.http.write_json({ ret = 1, sid = sid, enabled = enabled })
+ luci.http.write_json({ ret = 1, sid = sid, field = field, value = value })
end
function component_status()
@@ -1342,6 +1453,10 @@ function act_ping()
})
end
+ if e.ping then
+ e.ping = math.floor(e.ping + 0.5)
+ end
+
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua
index 76f8a6e9..32b6ba05 100644
--- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua
+++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua
@@ -142,7 +142,7 @@ local function set_apply_on_parse(map)
local old = map.on_after_save
map.on_after_save = function(self)
if old then old(self) end
- map:set("@global[0]", "timestamp", os.time())
+ --map:set("@global[0]", "timestamp", os.time())
end
end
end
diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua
index 8fa3f19e..92a8917b 100644
--- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua
+++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/servers.lua
@@ -64,7 +64,7 @@ local function set_apply_on_parse(map)
local old = map.on_after_save
map.on_after_save = function(self)
if old then old(self) end
- map:set("@global[0]", "timestamp", os.time())
+ -- map:set("@global[0]", "timestamp", os.time())
end
end
end
@@ -552,8 +552,9 @@ s = m:section(TypedSection, "server_subscribe_item", translate("Subscribe URL"))
s.anonymous = true
s.addremove = true
s.sortable = true
-s.template = "cbi/tblsection"
-s.template_addremove = "shadowsocksr/subscribe_actions_footer"
+s.template = "shadowsocksr/subscribe_actions_footer"
+--s.template = "cbi/tblsection"
+--s.template_addremove = "shadowsocksr/subscribe_actions_footer"
s.description = translate("Manage multiple subscribe URLs, including Clash subscriptions. Only enabled entries are included when updating all subscriptions.")
o = s:option(Flag, "enabled", translate("Enable"))
diff --git a/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe.htm b/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe.htm
index 3ac4c9ce..14ce0b7d 100644
--- a/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe.htm
+++ b/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe.htm
@@ -199,16 +199,17 @@ local autostart = self.map and self.map.ssr_subscribe_autostart
}
function startSubscribe(options, done) {
+ options = options || {};
running = true;
updateLog('');
- setStatus('<%:Starting subscription update...%>', '#0072c3');
+ setStatus(options.title || '<%:Starting subscription update...%>', '#0072c3');
openModal();
clearPoll();
fetchLog();
pollTimer = window.setInterval(fetchLog, 1000);
- XHR.get('<%=subscribe_url%>', null, function(x, rv) {
+ XHR.get('<%=subscribe_url%>', options.sid ? { sid: options.sid } : null, function(x, rv) {
var ok = !!(rv && typeof rv.ret !== 'undefined' && Number(rv.ret) === 0);
if (typeof done === 'function') {
done(ok);
diff --git a/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm b/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm
index 22bd436f..e9d4e0e3 100644
--- a/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm
+++ b/luci-app-ssr-plus/luasrc/view/shadowsocksr/subscribe_actions_footer.htm
@@ -1,62 +1,295 @@
+<%-
+local rowcnt = 0
+
+local function rowstyle()
+ rowcnt = rowcnt + 1
+ if rowcnt % 2 == 0 then
+ return " cbi-rowstyle-1"
+ end
+ return " cbi-rowstyle-2"
+end
+
+local function width(o)
+ if o.width then
+ if type(o.width) == "number" then
+ return ' style="width:%dpx"' % o.width
+ end
+ return ' style="width:%s"' % o.width
+ end
+ return ""
+end
+-%>
+
+
+ <% if self.title and #self.title > 0 then -%>
+
<%=self.title%>
+ <%- end %>
+ <%- if self.sortable then -%>
+
+ <%- end -%>
+
<%=self.description%>
+
+
+ <% for _, node in ipairs(self.children) do if not node.optional and node.option ~= "_update" then -%>
+
><%=node.title%>
+ <% end end -%>
+
<%:Actions%>
+
+
+ <%
+ local isempty = true
+ for i, section in ipairs(self:cfgsections()) do
+ isempty = false
+ local colorclass = rowstyle()
+ local scope = {
+ valueheader = "cbi/cell_valueheader",
+ valuefooter = "cbi/cell_valuefooter"
+ }
+ -%>
+
+ <%
+ for _, node in ipairs(self.children) do
+ if not node.optional and node.option ~= "_update" then
+ node:render(section, scope)
+ end
+ end
+ local alias = self.map:get(section, "alias") or string.format("Subscribe %s", section:sub(-4))
+ local update_title = "Subscribe: " .. tostring(alias)
+ -%>
+