带 pinSHA256 的 hysteria2 节点用自签/SNI 伪装证书(如 sni=www.bing.com), 靠指纹固定校验而非标准 CA 链。Surge 的 server-cert-fingerprint-sha256 实测 不可靠(仍按 SNI 主机名校验并拒绝),故改为输出 skip-cert-verify=true, 与 Shadowrocket 等客户端的实际行为一致。 新增 regen-surge-lines.ts 一次性脚本:从 uri 重新生成所有静态节点的 surge_line(保留自定义名字),修正解析器改动前已存的节点。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
35 lines
1.7 KiB
TypeScript
35 lines
1.7 KiB
TypeScript
import type { ParsedNode } from './ss.js';
|
|
import { readInsecure } from './util.js';
|
|
|
|
// hysteria2://auth@host:port?sni=...&insecure=1&obfs=salamander&obfs-password=...#name
|
|
// hy2:// is an accepted alias for the scheme.
|
|
export function parseHysteria2(uri: string): ParsedNode {
|
|
const url = new URL(uri);
|
|
const server = url.hostname;
|
|
const port = parseInt(url.port || '443', 10);
|
|
const name = decodeURIComponent(url.hash.slice(1)) || 'Hysteria2';
|
|
|
|
// The auth string lives in the userinfo. For userpass auth it is "username:password".
|
|
const user = decodeURIComponent(url.username);
|
|
const pass = decodeURIComponent(url.password);
|
|
const auth = pass ? `${user}:${pass}` : user;
|
|
|
|
const params = url.searchParams;
|
|
const sni = params.get('sni') || '';
|
|
const obfs = params.get('obfs') || '';
|
|
const obfsPassword = params.get('obfs-password') || params.get('obfs_password') || '';
|
|
// pinSHA256 means the node uses a self-signed / SNI-masqueraded cert verified by
|
|
// fingerprint rather than the standard CA chain. Surge's server-cert-fingerprint-sha256
|
|
// proved unreliable here (it still validates against the SNI hostname and rejects the
|
|
// cert), so in that case we skip standard verification — matching the effective behavior
|
|
// of clients like Shadowrocket that connect fine via pinSHA256.
|
|
const hasPin = !!(params.get('pinSHA256') || params.get('pinsha256'));
|
|
|
|
let line = `${name} = hysteria2, ${server}, ${port}, password=${auth}`;
|
|
if (sni) line += `, sni=${sni}`;
|
|
if (obfs === 'salamander' && obfsPassword) line += `, salamander-password=${obfsPassword}`;
|
|
line += `, skip-cert-verify=${readInsecure(params) || hasPin}`;
|
|
|
|
return { name, type: 'hysteria2', server, port, surgeLine: line };
|
|
}
|