mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-03 10:59:34 +00:00
fix(outbound): carry ALPN, fingerprint and UDP mask when importing a Hysteria2 link (#4760)
parseHysteria2Link hardcoded alpn to h3 and never read fp, ech, or the fm (finalmask) param, so importing a Hysteria2 client URL as an outbound dropped the configured ALPN, fingerprint, and salamander UDP mask. Parse alpn (falling back to h3 only when absent), fp, ech, and the pcs pinned-cert key, and restore the UDP mask via applyFinalMaskParam.
This commit is contained in:
@@ -404,6 +404,7 @@ export function parseHysteria2Link(link: string): Raw | null {
|
||||
const address = url.hostname;
|
||||
const port = Number(url.port) || 443;
|
||||
const params = url.searchParams;
|
||||
const alpn = params.get('alpn');
|
||||
const stream: Raw = {
|
||||
network: 'hysteria',
|
||||
security: 'tls',
|
||||
@@ -412,13 +413,14 @@ export function parseHysteria2Link(link: string): Raw | null {
|
||||
},
|
||||
tlsSettings: {
|
||||
serverName: params.get('sni') ?? '',
|
||||
alpn: ['h3'],
|
||||
fingerprint: '',
|
||||
echConfigList: '',
|
||||
alpn: alpn ? alpn.split(',') : ['h3'],
|
||||
fingerprint: params.get('fp') ?? '',
|
||||
echConfigList: params.get('ech') ?? '',
|
||||
verifyPeerCertByName: '',
|
||||
pinnedPeerCertSha256: params.get('pinSHA256') ?? '',
|
||||
pinnedPeerCertSha256: params.get('pcs') ?? '',
|
||||
},
|
||||
};
|
||||
applyFinalMaskParam(stream, params);
|
||||
return {
|
||||
protocol: 'hysteria',
|
||||
tag: decodeRemark(url),
|
||||
|
||||
@@ -267,6 +267,32 @@ describe('parseHysteria2Link', () => {
|
||||
const out = parseHysteria2Link('hy2://auth@srv:443?sni=example.com');
|
||||
expect(out?.protocol).toBe('hysteria');
|
||||
});
|
||||
|
||||
it('parses alpn, fingerprint and the salamander UDP mask (fm) — #4760', () => {
|
||||
const link = 'hysteria2://78e7795a209c4c099f896a816fc8448f@news.domain.org:8443?'
|
||||
+ 'alpn=h2%2Chttp%2F1.1&'
|
||||
+ 'fm=%7B%22udp%22%3A%5B%7B%22settings%22%3A%7B%22password%22%3A%22ftwfgb9655hh2mgo%22%7D%2C%22type%22%3A%22salamander%22%7D%5D%7D&'
|
||||
+ 'fp=chrome&obfs=salamander&obfs-password=655hh2mgo&security=tls&sni=news.domain.org'
|
||||
+ '#hy2-ej596ty350qs';
|
||||
const out = parseHysteria2Link(link);
|
||||
expect(out).not.toBeNull();
|
||||
const stream = out!.streamSettings as Record<string, unknown>;
|
||||
const tls = stream.tlsSettings as Record<string, unknown>;
|
||||
expect(tls.alpn).toEqual(['h2', 'http/1.1']);
|
||||
expect(tls.fingerprint).toBe('chrome');
|
||||
expect(tls.serverName).toBe('news.domain.org');
|
||||
const finalmask = stream.finalmask as Record<string, unknown>;
|
||||
expect(finalmask).toBeDefined();
|
||||
const udp = finalmask.udp as Array<Record<string, unknown>>;
|
||||
expect(udp[0].type).toBe('salamander');
|
||||
expect((udp[0].settings as Record<string, unknown>).password).toBe('ftwfgb9655hh2mgo');
|
||||
});
|
||||
|
||||
it('defaults alpn to h3 when the link omits it', () => {
|
||||
const out = parseHysteria2Link('hysteria2://auth@srv:443?sni=example.com');
|
||||
const tls = (out!.streamSettings as Record<string, unknown>).tlsSettings as Record<string, unknown>;
|
||||
expect(tls.alpn).toEqual(['h3']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseVlessLink — extra / fm / x_padding_bytes (B20)', () => {
|
||||
|
||||
Reference in New Issue
Block a user