fix(outbounds): parse wireguard:// links and fix ss:// query-string port

Add parseWireguardLink to the outbound import dispatcher: maps the secretKey userinfo, peer publicKey/endpoint, address, mtu, reserved, preSharedKey and keepAlive (probing common client aliases). Previously any wireguard:// link fell through to null and showed "Wrong Link!".

Also fix parseShadowsocksLink so a trailing query string (e.g. ?type=tcp) no longer leaks into the host:port slice, which made Number(port) NaN and silently fell back to 443. Strip the query before parsing in both the modern and legacy ss forms.
This commit is contained in:
MHSanaei
2026-05-29 21:27:32 +02:00
parent cb7af04cd3
commit 12afb862ff
2 changed files with 132 additions and 5 deletions
+72 -5
View File
@@ -356,18 +356,20 @@ export function parseShadowsocksLink(link: string): Raw | null {
if (hashIndex >= 0) {
try { remark = decodeURIComponent(link.slice(hashIndex + 1)); } catch { remark = ''; }
}
const atIndex = linkNoHash.indexOf('@');
const queryIndex = linkNoHash.indexOf('?');
const core = queryIndex >= 0 ? linkNoHash.slice(0, queryIndex) : linkNoHash;
const atIndex = core.indexOf('@');
if (atIndex >= 0) {
try { userInfo = Base64.decode(linkNoHash.slice('ss://'.length, atIndex)); }
catch { userInfo = linkNoHash.slice('ss://'.length, atIndex); }
const hostPort = linkNoHash.slice(atIndex + 1);
try { userInfo = Base64.decode(core.slice('ss://'.length, atIndex)); }
catch { userInfo = core.slice('ss://'.length, atIndex); }
const hostPort = core.slice(atIndex + 1);
const colon = hostPort.lastIndexOf(':');
if (colon < 0) return null;
host = hostPort.slice(0, colon);
port = Number(hostPort.slice(colon + 1)) || 443;
} else {
let decoded: string;
try { decoded = Base64.decode(linkNoHash.slice('ss://'.length)); }
try { decoded = Base64.decode(core.slice('ss://'.length)); }
catch { return null; }
const at = decoded.indexOf('@');
if (at < 0) return null;
@@ -424,6 +426,70 @@ export function parseHysteria2Link(link: string): Raw | null {
};
}
function firstParam(params: URLSearchParams, ...keys: string[]): string | null {
for (const k of keys) {
const v = params.get(k);
if (v !== null && v !== '') return v;
}
return null;
}
export function parseWireguardLink(link: string): Raw | null {
const url = parseUrlLink(link, 'wireguard') ?? parseUrlLink(link, 'wg');
if (!url) return null;
let secretKey: string;
try {
secretKey = decodeURIComponent(url.username);
} catch {
secretKey = url.username;
}
const params = url.searchParams;
const host = url.hostname;
const port = url.port;
const endpoint = host ? (port ? `${host}:${port}` : host) : '';
const addressRaw = firstParam(params, 'address', 'ip') ?? '';
const address = addressRaw.split(',').map((s) => s.trim()).filter(Boolean);
const allowedRaw = firstParam(params, 'allowedips', 'allowed_ips');
const allowedIPs = allowedRaw
? allowedRaw.split(',').map((s) => s.trim()).filter(Boolean)
: ['0.0.0.0/0', '::/0'];
const peer: Raw = {
publicKey: firstParam(params, 'publickey', 'publicKey', 'public_key', 'peerPublicKey') ?? '',
endpoint,
allowedIPs,
};
const psk = firstParam(params, 'presharedkey', 'preshared_key', 'pre-shared-key', 'psk');
if (psk) peer.preSharedKey = psk;
const keepAliveRaw = firstParam(params, 'keepalive', 'persistentkeepalive', 'persistent_keepalive');
if (keepAliveRaw !== null) {
const k = Number(keepAliveRaw);
if (Number.isFinite(k)) peer.keepAlive = k;
}
const settings: Raw = { secretKey, address, peers: [peer] };
const mtuRaw = firstParam(params, 'mtu');
if (mtuRaw !== null) {
const m = Number(mtuRaw);
if (Number.isFinite(m)) settings.mtu = m;
}
const reservedRaw = firstParam(params, 'reserved');
if (reservedRaw) {
const reserved = reservedRaw.split(',')
.map((s) => Number(s.trim()))
.filter((n) => Number.isFinite(n));
if (reserved.length > 0) settings.reserved = reserved;
}
return {
protocol: 'wireguard',
tag: decodeRemark(url),
settings,
};
}
// Dispatcher — first non-null parser wins. Returns null when no parser
// recognizes the link's protocol scheme.
export function parseOutboundLink(link: string): Raw | null {
@@ -435,5 +501,6 @@ export function parseOutboundLink(link: string): Raw | null {
?? parseTrojanLink(trimmed)
?? parseShadowsocksLink(trimmed)
?? parseHysteria2Link(trimmed)
?? parseWireguardLink(trimmed)
);
}