Skip to content

Commit 07f5f05

Browse files
author
jiangwy
committed
fix: 更新ShadowsocksR协议支持状态为暂未支持,并优化SS协议的链接转换功能,兼容SIP002格式链接
1 parent 55acc76 commit 07f5f05

5 files changed

Lines changed: 94 additions & 6 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
| VMess || 完全支持 | 完全支持 | 已测试 |
2323
| Trojan || 完全支持 | 完全支持 | 已测试 |
2424
| Shadowsocks || 完全支持 | 完全支持 | 已测试 |
25-
| ShadowsocksR || 完全支持 | 完全支持 | 已测试 |
25+
| ShadowsocksR || 暂未支持 | 暂未支持 | 未测试 |
2626
| Hysteria || 完全支持 | 完全支持 | 已测试 |
2727
| Hysteria2 || 完全支持 | 完全支持 | 已测试 |
2828
| HY2 || 完全支持 | 完全支持 | 已测试 |

src/core/convert/formatPs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,4 @@ export class FormatPs {
7070
return null;
7171
}
7272
}
73+

src/core/parser/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class Parser extends Convert {
4343
parser = new VmessParser(processVps);
4444
} else if (processVps.startsWith('trojan://') && this.hasProtocol('trojan')) {
4545
parser = new TrojanParser(processVps);
46-
} else if (processVps.startsWith('ss://') && this.hasProtocol('shadowsocks', 'shadowsocksr')) {
46+
} else if (processVps.startsWith('ss://') && this.hasProtocol('shadowsocks')) {
4747
parser = new SsParser(processVps);
4848
} else if (this.isHysteria2(processVps) && this.hasProtocol('hysteria', 'hysteria2', 'hy2')) {
4949
parser = new Hysteria2Parser(processVps);

src/core/parser/protocol/ss.ts

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { SsConfig } from '../types';
2+
import { base64Decode, base64Encode } from 'cloudflare-tools';
23
import { Faker } from '../../../shared/faker';
34
import { PsUtil } from '../../../shared/ps';
45

@@ -35,8 +36,9 @@ export class SsParser extends Faker {
3536
* @param {string} v
3637
*/
3738
private setOriginConfig(v: string): void {
38-
this.#originLink = v;
39-
this.#originConfig = new URL(v);
39+
const _v = this.toStandard(v);
40+
this.#originLink = _v;
41+
this.#originConfig = new URL(_v);
4042
this.#originPs = this.#originConfig.hash ?? '';
4143
}
4244

@@ -56,13 +58,13 @@ export class SsParser extends Faker {
5658
* @param {string} v
5759
*/
5860
private setConfuseConfig(v: string): void {
59-
this.#confuseConfig = new URL(v);
61+
this.#confuseConfig = new URL(this.toStandard(v));
6062
this.#confuseConfig.username = this.getUsername();
6163
this.#confuseConfig.host = this.getHost();
6264
this.#confuseConfig.hostname = this.getHostName();
6365
this.#confuseConfig.port = this.getPort();
6466
this.#confuseConfig.hash = PsUtil.setPs(this.#originPs, this.#confusePs);
65-
this.#confuseLink = `ss://${decodeURIComponent(this.#originConfig.username!)}@${this.#confuseConfig.host}:${this.#confuseConfig.port}${this.#confuseConfig.search}#${this.#confuseConfig.hash}`;
67+
this.#confuseLink = `ss://${decodeURIComponent(this.#originConfig.username!)}@${this.#confuseConfig.hostname}:${this.#confuseConfig.port}${this.#confuseConfig.search}#${this.#confuseConfig.hash}`;
6668
}
6769

6870
public restoreClash(proxy: Record<string, string | number>, ps: string): Record<string, string | number> {
@@ -124,5 +126,87 @@ export class SsParser extends Faker {
124126
get confuseConfig(): Partial<SsConfig> {
125127
return this.#confuseConfig;
126128
}
129+
130+
/**
131+
* @description 将传统格式 (SIP002) 的链接转换为标准新版 URI 格式的链接
132+
* @param {string} sipUrl SIP002 格式链接
133+
* @returns {string} 标准新版 URI 格式链接
134+
*/
135+
private toStandard(sipUrl: string): string {
136+
// 检查是否为传统 SIP002 格式: ss://[base64(method:password@server:port)]#remark
137+
// 先提取 fragment (备注部分)
138+
const fragmentMatch = sipUrl.match(/#(.*)$/);
139+
const fragment = fragmentMatch ? `#${fragmentMatch[1]}` : '';
140+
141+
// 移除 fragment 部分,只保留主体
142+
const mainUrl = sipUrl.replace(/#.*$/, '');
143+
144+
// 检查是否为 ss:// 开头
145+
if (!mainUrl.startsWith('ss://')) {
146+
return sipUrl;
147+
}
148+
149+
// 提取 base64 编码部分
150+
const base64Part = mainUrl.substring(5); // 移除 'ss://'
151+
152+
// 检查是否包含 @ 符号,如果包含说明已经是标准格式
153+
if (base64Part.includes('@')) {
154+
return sipUrl;
155+
}
156+
157+
try {
158+
// 解码 base64 获取完整配置: method:password@server:port
159+
const decodedConfig = base64Decode(base64Part);
160+
161+
// 解析格式: method:password@server:port
162+
const atIndex = decodedConfig.lastIndexOf('@');
163+
if (atIndex === -1) {
164+
throw new Error('Invalid SIP002 format: missing @ separator');
165+
}
166+
167+
const userInfo = decodedConfig.substring(0, atIndex); // method:password
168+
const serverPart = decodedConfig.substring(atIndex + 1); // server:port
169+
170+
const colonIndex = userInfo.indexOf(':');
171+
if (colonIndex === -1) {
172+
throw new Error('Invalid user info: missing colon separator');
173+
}
174+
175+
const method = userInfo.substring(0, colonIndex);
176+
const password = userInfo.substring(colonIndex + 1);
177+
178+
// 解析服务器和端口
179+
const lastColonIndex = serverPart.lastIndexOf(':');
180+
if (lastColonIndex === -1) {
181+
throw new Error('Invalid server info: missing port');
182+
}
183+
184+
const server = serverPart.substring(0, lastColonIndex);
185+
const port = serverPart.substring(lastColonIndex + 1);
186+
187+
if (!method || !password || !server || !port) {
188+
throw new Error('Invalid format: missing required fields');
189+
}
190+
191+
// 构造标准新版 URI 格式的 userinfo (method:password)
192+
const newUserInfo = `${method}:${password}`;
193+
const encodedNewUserInfo = base64Encode(newUserInfo);
194+
195+
// 构造标准新版 URI 格式
196+
let newUrl = `ss://${encodedNewUserInfo}@${server}:${port}`;
197+
198+
// 添加默认参数 type=tcp
199+
newUrl += '?type=tcp';
200+
201+
// 添加 fragment (备注)
202+
if (fragment) {
203+
newUrl += fragment;
204+
}
205+
206+
return newUrl;
207+
} catch {
208+
return sipUrl;
209+
}
210+
}
127211
}
128212

src/core/parser/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ export interface TrojanConfig extends BaseConfig {}
4545

4646
export interface SsConfig extends BaseConfig {}
4747

48+
export interface SsrConfig extends BaseConfig {}
49+
4850
export interface Hysteria2Config extends BaseConfig {}
4951

5052
export type ParserType = VlessParser | VmessParser | TrojanParser | SsParser | Hysteria2Parser;
53+

0 commit comments

Comments
 (0)