update 2026-06-21 21:58:17

This commit is contained in:
action committed 2026-06-21 21:58:17 +08:00
1 parent 1a2f3e74c1
commit dc85f71cc1
12 files changed
+1227 -936

No files matched your search

+3 -5
View File
@@ -10,14 +10,12 @@ npu_usage="$(grep '%' "$NSS_PATH"/cpu_load_ubi | awk -F ' ' '{print $2}')"
if [ -e "$ATH11K_NSS_OFFLOAD" ]; then
if [ "$(cat "$ATH11K_NSS_OFFLOAD" 2>/dev/null)" = "1" ]; then
wifi_nss=", WiFi NSS: ON"
else
wifi_nss=", WiFi NSS: OFF"
wifi="with WiFi"
fi
fi
if [ -d "$NSS_PATH" ]; then
echo -n "CPU: ${cpu_usage}, NPU: ${npu_usage}${wifi_nss}"
echo -n "CPU: ${cpu_usage}, NPU${wifi}: ${npu_usage}"
else
echo -n "CPU: ${cpu_usage}${wifi_nss}"
echo -n "CPU: ${cpu_usage}"
fi
+1
View File
@@ -107,6 +107,7 @@ choice
config PACKAGE_$(PKG_NAME)_INCLUDE_Xray
bool "Xray-core"
select PACKAGE_xray-core
select PACKAGE_coreutils-timeout
endchoice
config PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG
@@ -661,6 +661,8 @@ function index()
entry({"admin", "services", "shadowsocksr", "clash_client_policies"}, call("clash_client_policies")).leaf = true
entry({"admin", "services", "shadowsocksr", "clash_client_rule_save"}, call("clash_client_rule_save")).leaf = true
entry({"admin", "services", "shadowsocksr", "clash_client_rule_clear"}, call("clash_client_rule_clear")).leaf = true
entry({"admin", "services", "shadowsocksr", "fetch_certsha256"}, call("fetch_certsha256")).leaf = true
entry({"admin", "services", "shadowsocksr", "fetch_certbyname"}, call("fetch_certbyname")).leaf = true
--[[Backup]]
entry({"admin", "services", "shadowsocksr", "backup"}, call("create_backup")).leaf = true
end
@@ -1271,6 +1273,84 @@ function clash_client_rule_clear()
})
end
function fetch_certsha256()
local function fetch_cert_sha256(host, port, sni, timeout)
if not host then return "" end
port = tonumber(port) or 443
sni = sni or host
timeout = tonumber(timeout) or 5
local cmd = string.format(
"timeout %d openssl s_client -connect %s:%d -servername %s -showcerts </dev/null 2>/dev/null " ..
"| awk 'BEGIN{c=0}/BEGIN CERT/{c++} c==1{print} /END CERT/{if(c==1)exit}' " ..
"| openssl x509 -outform der 2>/dev/null " ..
"| sha256sum 2>/dev/null",
timeout, host, port, sni
)
local out = trim(luci.sys.exec(cmd))
local fp = out:match("^([0-9a-fA-F]+)")
if not fp or fp:lower():match("^e3b0c44298fc1c149afbf4c8996fb924") then
return ""
end
return fp:upper()
end
local sid = luci.http.formvalue("sid") or ""
local address = (sid ~= "") and uci:get("shadowsocksr", sid, "server") or ""
local port_raw = (sid ~= "") and uci:get("shadowsocksr", sid, "server_port") or ""
local port = tonumber(port_raw) or 0
local sni = (id ~= "") and uci:get("shadowsocksr", sid, "tls_host") or ""
sni = (sni and sni ~= "") and sni or address
if address == "" or port == 0 then
luci.http.prepare_content("application/json")
luci.http.write_json({ code = 0, msg = "Address or Port is invalid" })
return
end
local data = fetch_cert_sha256(address, port, sni, 5)
luci.http.prepare_content("application/json")
luci.http.write_json(data ~= "" and { code = 1, data = data } or { code = 0 })
end
function fetch_certbyname()
local function fetch_cert_byname(host, port, sni, timeout)
if not host then return "" end
port = tonumber(port) or 443
sni = sni or host
timeout = tonumber(timeout) or 5
local cmd = string.format(
"timeout %d openssl s_client -connect %s:%d -servername %s -showcerts </dev/null 2>/dev/null " ..
"| openssl x509 -noout -subject 2>/dev/null " ..
"| awk '{gsub(/^.*=[[:space:]]*/, \"\"); gsub(/,.*$/, \"\"); print}'",
timeout, host, port, sni
)
local out = trim(luci.sys.exec(cmd))
if out == "" then
return ""
end
return out
end
local sid = luci.http.formvalue("sid") or ""
local address = (sid ~= "") and uci:get("shadowsocksr", sid, "server") or ""
local port_raw = (sid ~= "") and uci:get("shadowsocksr", sid, "server_port") or ""
local port = tonumber(port_raw) or 0
local sni = (id ~= "") and uci:get("shadowsocksr", sid, "tls_host") or ""
sni = (sni and sni ~= "") and sni or address
if address == "" or port == 0 then
luci.http.prepare_content("application/json")
luci.http.write_json({ code = 0, msg = "Address or Port is invalid" })
return
end
local data = fetch_cert_byname(address, port, sni, 5)
luci.http.prepare_content("application/json")
luci.http.write_json(data ~= "" and { code = 1, data = data } or { code = 0 })
end
function act_ping()
local e = {}
local domain = luci.http.formvalue("domain")
@@ -321,6 +321,10 @@ m.on_after_save = function(self)
end
end
local server_header = Template("/shadowsocksr/server_header")
server_header.section = sid
m:append(server_header)
-- [[ Servers Setting ]]--
s = m:section(NamedSection, sid, "servers")
s.anonymous = true
@@ -1542,14 +1546,18 @@ end
if xray_version_val >= 260131 then
-- Xray 版本大于等于 26.1.31
-- [[ Xray TLS pinSHA256 ]] --
o = s:option(Value, "tls_CertSha", translate("TLS Chain Fingerprint (SHA256)"), translate("Once set, connects only when the server’s chain fingerprint matches."))
o = s:option(Value, "tls_CertSha", translate("TLS Chain Fingerprint (SHA256)"))
o.rmempty = true
o:depends({type = "v2ray", tls = true})
o.description = translate("Once set, connects only when the server’s chain fingerprint matches.") ..
string.format("<a href='javascript:void(0)' onclick='javascript:fetchCertSha256(this)'>%s</a>", "→ " .. translate("Fetch Manually"))
-- [[ Xray TLS verify leaf certificate name ]] --
o = s:option(Value, "tls_CertByName", translate("TLS Certificate Name (CertName)"), translate("TLS is used to verify the leaf certificate name."))
o = s:option(Value, "tls_CertByName", translate("TLS Certificate Name (CertName)"))
o.rmempty = true
o:depends({type = "v2ray", tls = true})
o.description = translate("TLS is used to verify the leaf certificate name.") ..
string.format("<a href='javascript:void(0)' onclick='javascript:fetchCertByName(this)'>%s</a>", "→ " .. translate("Fetch Manually"))
end
-- [[ Hysteria2 TLS pinSHA256 ]] --
@@ -4,7 +4,7 @@ local dsp = require "luci.dispatcher"
<script type="text/javascript">
//<![CDATA[
function clearlog(btn) {
XHR.get('<%=dsp.build_url("admin/services/shadowsocksr/clear_log")%>', null,
XHR.get('<%=dsp.build_url("admin", "services", "shadowsocksr", "clear_log")%>', null,
function(x, data) {
if (x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
@@ -15,7 +15,7 @@ local dsp = require "luci.dispatcher"
);
}
XHR.poll(5, '<%=dsp.build_url("admin/services/shadowsocksr/get_log")%>', null,
XHR.poll(5, '<%=dsp.build_url("admin", "services", "shadowsocksr", "get_log")%>', null,
function(x, data) {
if (x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
@@ -0,0 +1,78 @@
<script type="text/javascript">
//<![CDATA[
let sid = "<%=self.section%>";
function fetchCertSha256(el) {
if (el.getAttribute("data-loading") === "1") return;
el.setAttribute("data-loading", "1");
el.style.pointerEvents = "none";
el.style.color = "red";
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr", "fetch_certsha256")%>',
{ sid: sid },
function(x, res) {
el.setAttribute("data-loading", "0");
el.style.pointerEvents = "";
el.style.color = "";
if (!x || x.status !== 200) {
alert("<%:Error%>");
return;
}
if (!res || res.code !== 1 || !res.data) {
alert(res && res.msg ? res.msg : "<%:Fetch Failed%> !");
return;
}
const inputs = document.querySelectorAll('input[id$=".tls_CertSha"]');
if (inputs.length > 0) {
inputs.forEach(function(input) {
input.value = res.data;
// 触发 change 事件,让 LuCI 检测到字段已修改
const event = new Event('change', { bubbles: true });
input.dispatchEvent(event);
});
// 成功提示(只有找到输入框时才提示)
alert(res && res.msg ? res.msg : "<%:Successfully retrieved certificate fingerprint:%> " + res.data);
} else {
// 未找到输入框时提示
alert("<%:Certificate fingerprint retrieved successfully, but no input field found!%>");
}
});
}
function fetchCertByName(el) {
if (el.getAttribute("data-loading") === "1") return;
el.setAttribute("data-loading", "1");
el.style.pointerEvents = "none";
el.style.color = "red";
XHR.get
('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr", "fetch_certbyname")%>',
{ sid: sid },
function(x, res) {
el.setAttribute("data-loading", "0");
el.style.pointerEvents = "";
el.style.color = "";
if (!x || x.status !== 200) {
alert("<%:Error%>");
return;
}
if (!res || res.code !== 1 || !res.data) {
alert(res && res.msg ? res.msg : "<%:Fetch Failed%> !");
return;
}
const inputs = document.querySelectorAll('input[id$=".tls_CertByName"]');
if (inputs.length > 0) {
inputs.forEach(function(input) {
input.value = res.data;
// 触发 change 事件,让 LuCI 检测到字段已修改
const event = new Event('change', { bubbles: true });
input.dispatchEvent(event);
});
// 成功提示(只有找到输入框时才提示)
alert(res && res.msg ? res.msg : "<%:Successfully retrieved certificate name:%> " + res.data);
} else {
// 未找到输入框时提示
alert("<%:Certificate name retrieved successfully, but no input field found!%>");
}
});
}
//]]>
</script>
@@ -165,7 +165,7 @@ end
return;
}
port.innerHTML = '<font style=\"color:#0072c3\">connect</font>';
XHR.get('<%=luci.dispatcher.build_url("admin/services/shadowsocksr/ping")%>', {
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr", "ping")%>', {
index,
sid: dom.getAttribute("data-sid"),
domain: dom.getAttribute("hint"),
@@ -280,7 +280,7 @@ end
var server_page = pageElem ? pageElem.value : '<%=self.server_page or 1%>';
var server_page_size = pageSizeElem ? pageSizeElem.value : '<%=self.server_page_size or 0%>';
if (input) input.value = order;
XHR.post('<%=luci.dispatcher.build_url("admin/services/shadowsocksr/save_order")%>', {
XHR.post('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr", "save_order")%>', {
order: order,
page: server_page,
page_size: server_page_size
@@ -248,7 +248,7 @@ end
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Delete%>"
onclick="
var sid = '<%=k%>';
XHR.post('<%=luci.dispatcher.build_url("admin/services/shadowsocksr/delete_node")%>', {
XHR.post('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr", "delete_node")%>', {
sid: sid
}, function(x, result) {
result = result || {};
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -50,32 +50,28 @@ local b64encode = nixio.bin.b64encode
local effective_node_local_port = tonumber(server.local_port) or tonumber(default_node_local_port) or 1234
if server.type == "ss-rust" then
server.type = "ss"
server.type = "ss"
end
local function parse_realm_uri(uri)
if type(uri) ~= "string" then return nil end
-- realm://token@server/realm_id?query
local token, server_url, realm_id, query = trim(uri):match("^realm://([^@]+)@([^/]+)/([^?]*)%??(.*)$")
if not token or not server_url or not realm_id then return nil end
-- realm[+http]://token@server/realm_id?query
local scheme, token, server_url, realm_id, query = trim(uri):match("^(realm%+http|realm)://([^@]+)@([^/]+)/([^?]*)%??(.*)$")
if not scheme or not token or not server_url or not realm_id then return nil end
realm_id = realm_id:gsub("/+$", "")
local realm = {
scheme = scheme,
token = token,
server_url = server_url,
realm_id = realm_id
}
-- 解析 query 中的 stun=
if query and query ~= "" then
local stun_servers = {}
for key, value in query:gmatch("([^&=?]+)=([^&]+)") do
if key == "stun" and value ~= "" then
stun_servers[#stun_servers + 1] = value
end
end
if #stun_servers > 0 then
realm.stun_servers = stun_servers
end
local stun_servers
for value in (query or ""):gmatch("[?&]?[Ss][Tt][Uu][Nn]=([^&]+)") do
if not stun_servers then stun_servers = {} end
stun_servers[#stun_servers + 1] = value
end
realm.stun_servers = stun_servers
return realm
end
@@ -428,22 +424,24 @@ end
-- 开启 socks 代理
-- 检查是否启用 socks 代理
if proto and proto:find("tcp") and socks_port ~= "0" then
table.insert(Xray.inbounds, {
-- socks
protocol = "socks",
port = tonumber(socks_port),
local auth = (socks_server.socks5_auth and socks_server.socks5_auth ~= "noauth")
and {{
user = socks_server.socks5_user,
pass = socks_server.socks5_pass
}} or nil
table.insert(Xray.inbounds, {
-- socks
protocol = "socks",
port = tonumber(socks_port),
settings = {
auth = socks_server.socks5_auth or "noauth",
udp = true,
mixed = ((socks_server.socks5_mixed == '1') and true or false) or nil,
accounts = (socks_server.socks5_auth and socks_server.socks5_auth ~= "noauth") and {
{
user = socks_server.socks5_user,
pass = socks_server.socks5_pass
}
} or nil
} or nil
})
mixed = socks_server.socks5_mixed == "1" or nil,
accounts = (xray_version_val <= 260503) and auth or nil,
users = (xray_version_val > 260503) and auth or nil
}
})
end
-- 传出连接
@@ -648,9 +646,7 @@ Xray.outbounds = {
local realm = parse_realm_uri(server.hysteria2_realm_url)
local url, stun
if realm then
if realm.token and realm.server_url and realm.realm_id then
url = "realm://" .. realm.token .. "@" .. realm.server_url .. "/" .. realm.realm_id
end
url = realm.scheme .. "://" .. realm.token .. "@" .. realm.server_url .. "/" .. realm.realm_id
stun = realm.stun_servers or server.hysteria2_realm_stun
end
local r = {
+2 -2
View File
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=a322dfb6bfd8987c83453c39582a07771c9deddf9f8f7d2d19c7927ebd5e76c8
endef
GEOSITE_VER:=20260621035818
GEOSITE_VER:=20260621074522
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
HASH:=3aa6a23a6f4bd89cf5b22348fd6527b5b53881cdeece3147ee3ac3381fe61ec2
HASH:=680b8a378b94b69e6398cd05d5b5b016cb0809fad30f385211ce91a5a0f74815
endef
GEOSITE_IRAN_VER:=202606150209