UNPKG

@herbertgao/surgio

Version:

Generating rules for Surge, Clash, Quantumult like a PRO

94 lines 3.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.stringifySip003Options = exports.parseSSUri = void 0; const url_1 = require("url"); const logger_1 = require("@surgio/logger"); const types_1 = require("../types"); const index_1 = require("./index"); const logger = (0, logger_1.createLogger)({ service: 'surgio:utils:ss' }); const parseSSUri = (str) => { logger.debug('Shadowsocks URI', str); const scheme = new url_1.URL(str); const pluginString = scheme.searchParams.get('plugin'); // SIP002 兼容:若 URL 已经拆分出 username/password,则说明 userinfo 为明文形式 let userInfo; if (scheme.password) { userInfo = [ decodeURIComponent(scheme.username), decodeURIComponent(scheme.password), ]; } else { userInfo = (0, index_1.fromUrlSafeBase64)(decodeURIComponent(scheme.username)).split(':'); } const pluginInfo = typeof pluginString === 'string' ? (0, index_1.decodeStringList)(pluginString.split(';')) : {}; // SIP001 兼容:如果未能正确取得 method 或 password,且未出现 '@',尝试解析整段 Base64 主机名 if (!userInfo[0] || userInfo.length < 2) { try { const legacyStr = (0, index_1.fromUrlSafeBase64)(scheme.hostname); // legacyStr 形如 method:password@host:port const atIndex = legacyStr.indexOf('@'); if (atIndex > 0) { const [cred, hostPort] = [legacyStr.slice(0, atIndex), legacyStr.slice(atIndex + 1)]; const [legacyMethod, legacyPassword] = cred.split(':'); const [legacyHost, legacyPort] = hostPort.split(':'); if (legacyMethod && legacyPassword && legacyHost && legacyPort) { userInfo = [legacyMethod, legacyPassword]; // 覆盖 hostname、port scheme.hostname = legacyHost; scheme.port = legacyPort; } } } catch (_) { // ignore } } return { type: types_1.NodeTypeEnum.Shadowsocks, nodeName: decodeURIComponent(scheme.hash.replace('#', '')), hostname: scheme.hostname, port: scheme.port, method: userInfo[0], password: userInfo[1], ...(pluginInfo['obfs-local'] ? { obfs: pluginInfo.obfs, obfsHost: pluginInfo['obfs-host'] + '', } : null), ...(pluginInfo['simple-obfs'] ? { obfs: pluginInfo.obfs, obfsHost: pluginInfo['obfs-host'] + '', } : null), ...(pluginInfo['v2ray-plugin'] ? { obfs: pluginInfo.tls ? 'wss' : 'ws', obfsHost: pluginInfo.host + '', } : null), }; }; exports.parseSSUri = parseSSUri; // Marshal SIP003 plugin options in PossibleNodeConfigType to formatted string. // An example is 'a=123;host=https://a.com/foo?bar\=baz&q\\q\=1&w\;w\=2;mode=quic;tls=true', // where semicolons, equal signs and backslashes MUST be escaped with a backslash. const stringifySip003Options = (args) => { if (!args) { return ''; } const keys = Object.keys(args).sort(); const pairs = []; for (const key of keys) { pairs.push(`${key.replace(/([;=\\])/g, '\\$1')}=${args[key] .toString() .replace(/([;=\\])/g, '\\$1')}`); } return pairs.join(';'); }; exports.stringifySip003Options = stringifySip003Options; //# sourceMappingURL=ss.js.map