update 2026-07-16 14:12:35
BIN
doc/lucky1.png
|
Before Width: | Height: | Size: 91 KiB |
BIN
doc/lucky2.png
|
Before Width: | Height: | Size: 319 KiB |
BIN
doc/lucky3.png
|
Before Width: | Height: | Size: 389 KiB |
BIN
doc/taskplan1.png
Normal file
|
After Width: | Height: | Size: 640 KiB |
BIN
doc/taskplan2.png
Normal file
|
After Width: | Height: | Size: 550 KiB |
BIN
doc/taskplan3.png
Normal file
|
After Width: | Height: | Size: 547 KiB |
BIN
doc/view.png
Normal file
|
After Width: | Height: | Size: 659 KiB |
BIN
doc/view2.png
Normal file
|
After Width: | Height: | Size: 650 KiB |
@ -1075,10 +1075,20 @@ function fetch_certsha256()
|
||||
local port = (id ~= "") and uci:get(appname, id, "port") or 0
|
||||
local sni = (id ~= "") and uci:get(appname, id, "tls_serverName") or ""
|
||||
sni = (sni ~= "") and sni or address
|
||||
local protocol = uci:get(appname, id, "protocol")
|
||||
local h3, timeout = false, 10
|
||||
if protocol == "hysteria2" then
|
||||
h3 = true
|
||||
timeout = 60
|
||||
if port == 0 then
|
||||
local hop = uci:get(appname, id, "hysteria2_hop") or "0"
|
||||
port = tonumber(hop:match("^%s*(%d+)"))
|
||||
end
|
||||
end
|
||||
if address == "" or port == 0 then
|
||||
http_write_json_error()
|
||||
return
|
||||
end
|
||||
local data = api.fetch_cert_sha256(address, port, sni, 5)
|
||||
local data = api.fetch_cert_sha256(address, port, sni, timeout, h3)
|
||||
http_write_json(data ~= "" and { code = 1, data = data } or { code = 0 })
|
||||
end
|
||||
|
||||
@ -1673,18 +1673,29 @@ function cleanEmptyTables(t)
|
||||
return next(t) and t or nil
|
||||
end
|
||||
|
||||
function fetch_cert_sha256(host, port, sni, timeout)
|
||||
function fetch_cert_sha256(host, port, sni, timeout, http3)
|
||||
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 cmd
|
||||
if http3 then
|
||||
cmd = string.format(
|
||||
"timeout %d curl --http3 -k -w '%%{certs}' -o /dev/null https://%s:%d 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
|
||||
)
|
||||
else
|
||||
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
|
||||
)
|
||||
end
|
||||
local out = trim(sys.exec(cmd))
|
||||
local fp = out:match("^([0-9a-fA-F]+)")
|
||||
if not fp or fp:lower():match("^e3b0c44298fc1c149afbf4c8996fb924") then
|
||||
|
||||
@ -153,8 +153,8 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
tlsSettings = (node.stream_security == "tls") and {
|
||||
serverName = node.tls_serverName,
|
||||
fingerprint = (node.type == "Xray" and node.utls == "1" and node.fingerprint and node.fingerprint ~= "") and node.fingerprint or nil,
|
||||
pinnedPeerCertSha256 = node.tls_pinSHA256,
|
||||
verifyPeerCertByName = node.tls_CertByName,
|
||||
pinnedPeerCertSha256 = node.tls_pinSHA256 or "",
|
||||
verifyPeerCertByName = node.tls_CertByName or "",
|
||||
echConfigList = (node.ech == "1") and node.ech_config or nil,
|
||||
certificates = (node.tls_certificate == "1" and node.tls_certificate_pem ~= "") and {
|
||||
certificate = api.split(node.tls_certificate_pem, "\n"),
|
||||
|
||||
@ -67,7 +67,10 @@ local api = self.api
|
||||
return;
|
||||
}
|
||||
if (!res || res.code !== 1 || !res.data) {
|
||||
alert(res && res.msg ? res.msg : "<%:Fetch Failed%> !");
|
||||
alert(res && res.msg ? res.msg : "<%:Fetch Failed!%>\n" +
|
||||
"<%:Requires OpenSSL to retrieve the TLS certificate chain fingerprint.%>\n" +
|
||||
"<%:QUIC protocols such as HY2 also require curl with http3 support.%>"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const inputs = document.querySelectorAll('input[id$=".xray_tls_pinSHA256"]');
|
||||
|
||||
@ -1820,8 +1820,14 @@ msgstr "TLS 用于验证 leaf 证书的 name。"
|
||||
msgid "Fetch Manually"
|
||||
msgstr "手动获取"
|
||||
|
||||
msgid "Fetch Failed"
|
||||
msgstr "获取失败"
|
||||
msgid "Fetch Failed!"
|
||||
msgstr "获取失败!"
|
||||
|
||||
msgid "Requires OpenSSL to retrieve the TLS certificate chain fingerprint."
|
||||
msgstr "获取 TLS 证书链指纹需要 OpenSSL 支持。"
|
||||
|
||||
msgid "QUIC protocols such as HY2 also require curl with http3 support."
|
||||
msgstr "HY2 等 QUIC 协议还需要 curl 支持 http3。"
|
||||
|
||||
msgid "TLS Certificate (PEM)"
|
||||
msgstr "TLS 证书(PEM)"
|
||||
|
||||
@ -177,8 +177,8 @@ function gen_outbound(flag, node, tag, proxy_table)
|
||||
tlsSettings = (node.stream_security == "tls") and {
|
||||
serverName = node.tls_serverName,
|
||||
fingerprint = (node.type == "Xray" and node.utls == "1" and node.fingerprint and node.fingerprint ~= "") and node.fingerprint or nil,
|
||||
pinnedPeerCertSha256 = node.tls_pinSHA256,
|
||||
verifyPeerCertByName = node.tls_CertByName,
|
||||
pinnedPeerCertSha256 = node.tls_pinSHA256 or "",
|
||||
verifyPeerCertByName = node.tls_CertByName or "",
|
||||
echConfigList = (node.ech == "1") and node.ech_config or nil,
|
||||
certificates = (node.tls_certificate == "1" and node.tls_certificate_pem ~= "") and {
|
||||
certificate = api.split(node.tls_certificate_pem, "\n"),
|
||||
|
||||