@herbertgao/surgio
Version:
Generating rules for Surge, Clash, Quantumult like a PRO
94 lines • 3.65 kB
JavaScript
;
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