Skip to content

Commit 3e5ae47

Browse files
committed
v2.9.2: 添加小火箭格式支持,适配所有协议的ECH功能
1 parent 48ade67 commit 3e5ae47

File tree

2 files changed

+122
-4
lines changed

2 files changed

+122
-4
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# CFnew - 终端 v2.9.1
1+
# CFnew - 终端 v2.9.2
22

33
**语言:** [中文](README.md) | [فارسی](فارسی.md)
44

@@ -17,6 +17,12 @@
1717
- 自动识别:根据User-Agent自动返回对应格式
1818
- 多语言:支持中文和波斯语,根据浏览器语言自动切换
1919

20+
## v2.9.2 更新
21+
22+
- **小火箭格式支持**:自动适配 Shadowrocket 客户端格式,支持 vless、trojan、xhttp 协议
23+
- **ECH 小火箭适配**:ECH 功能在小火箭格式中正确转换,包含所有必要参数
24+
- **自动格式识别**:点击 Shadowrocket 按钮时自动添加格式参数
25+
2026
## v2.9.1 更新
2127

2228
- ECH支持:新增 Encrypted Client Hello (ECH) 功能

明文源吗

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,104 @@
12061206
return btoa(links.join('\n'));
12071207
}
12081208

1209+
function generateV2RayConfig(links) {
1210+
return btoa(links.join('\n'));
1211+
}
1212+
1213+
// 生成小火箭格式的订阅(支持 vless、trojan、xhttp 协议,针对ECH进行适配)
1214+
function generateShadowrocketConfig(links) {
1215+
const shadowrocketLinks = links.map(link => {
1216+
try {
1217+
// 解析 vless:// 或 trojan:// 链接
1218+
const urlMatch = link.match(/^(vless|trojan):\/\/([^@]+)@([^?#]+)(\?[^#]*)?(#.*)?$/);
1219+
if (!urlMatch) return link; // 如果无法解析,返回原链接
1220+
1221+
const protocol = urlMatch[1];
1222+
const user = urlMatch[2];
1223+
const address = urlMatch[3];
1224+
const queryString = urlMatch[4] || '';
1225+
const fragment = urlMatch[5] || '';
1226+
1227+
// 解析地址和端口
1228+
const addrMatch = address.match(/^\[?([^\]]+)\]?:(\d+)$/);
1229+
if (!addrMatch) return link;
1230+
const host = addrMatch[1];
1231+
const port = addrMatch[2];
1232+
1233+
// 解析查询参数
1234+
const params = new URLSearchParams(queryString.substring(1));
1235+
const remarks = fragment.substring(1) ? decodeURIComponent(fragment.substring(1)) : '';
1236+
1237+
// 构建小火箭格式的用户部分(base64编码 auto:user)
1238+
// 注意:小火箭使用 base64 编码,但不需要移除 = 号,URL 会自动处理
1239+
const shadowrocketUser = btoa(`auto:${user}`);
1240+
1241+
// 构建小火箭格式的参数
1242+
const shadowrocketParams = new URLSearchParams();
1243+
1244+
// path 参数
1245+
const path = params.get('path') || '/?ed=2048';
1246+
shadowrocketParams.set('path', path);
1247+
1248+
// remarks 参数
1249+
if (remarks) {
1250+
shadowrocketParams.set('remarks', remarks);
1251+
}
1252+
1253+
// obfsParam 参数(从 host 参数获取)
1254+
const obfsParam = params.get('host') || host;
1255+
shadowrocketParams.set('obfsParam', obfsParam);
1256+
1257+
// obfs 参数(根据 type 参数设置)
1258+
const type = params.get('type');
1259+
if (type === 'ws') {
1260+
shadowrocketParams.set('obfs', 'websocket');
1261+
} else if (type === 'xhttp') {
1262+
shadowrocketParams.set('obfs', 'http');
1263+
}
1264+
1265+
// tls 参数
1266+
if (params.get('security') === 'tls') {
1267+
shadowrocketParams.set('tls', '1');
1268+
}
1269+
1270+
// peer 参数(从 sni 参数获取)
1271+
const peer = params.get('sni') || obfsParam;
1272+
shadowrocketParams.set('peer', peer);
1273+
1274+
// alpn 参数(如果有,通常启用ECH时会有)
1275+
const alpn = params.get('alpn');
1276+
if (alpn) {
1277+
shadowrocketParams.set('alpn', alpn);
1278+
}
1279+
1280+
// fragment 和 mode 参数
1281+
shadowrocketParams.set('fragment', 'auto');
1282+
shadowrocketParams.set('mode', 'auto');
1283+
1284+
// ech 参数(只针对ECH,如果有)
1285+
const ech = params.get('ech');
1286+
if (ech) {
1287+
shadowrocketParams.set('ech', ech);
1288+
}
1289+
1290+
// 对于 xhttp 协议,需要添加 mode 参数(如果原链接有)
1291+
const mode = params.get('mode');
1292+
if (mode) {
1293+
shadowrocketParams.set('mode', mode);
1294+
}
1295+
1296+
// 构建小火箭格式的链接(保持原协议类型)
1297+
return `${protocol}://${shadowrocketUser}@${host}:${port}?${shadowrocketParams.toString()}`;
1298+
} catch (error) {
1299+
// 如果转换失败,返回原链接
1300+
return link;
1301+
}
1302+
});
1303+
1304+
return btoa(shadowrocketLinks.join('\n'));
1305+
}
1306+
12091307
// 全局变量存储ECH调试信息
12101308
let echDebugInfo = '';
12111309

@@ -1360,7 +1458,13 @@
13601458

13611459
const finalLinks = [];
13621460
const workerDomain = url.hostname;
1363-
const target = url.searchParams.get('target') || 'base64';
1461+
1462+
// 检测 User-Agent 或 target 参数来确定是否为小火箭
1463+
const userAgent = request.headers.get('User-Agent') || '';
1464+
const isShadowrocket = userAgent.toLowerCase().includes('shadowrocket') ||
1465+
url.searchParams.get('target')?.toLowerCase() === 'shadowrocket';
1466+
1467+
const target = isShadowrocket ? 'shadowrocket' : (url.searchParams.get('target') || 'base64');
13641468

13651469
// 获取ECH配置(如果启用)
13661470
let echConfig = null;
@@ -1512,6 +1616,9 @@
15121616
case atob('bG9vbg=='):
15131617
subscriptionContent = generateLoonConfig(finalLinks);
15141618
break;
1619+
case 'shadowrocket':
1620+
subscriptionContent = generateShadowrocketConfig(finalLinks);
1621+
break;
15151622
default:
15161623
subscriptionContent = btoa(finalLinks.join('\n'));
15171624
}
@@ -2177,7 +2284,7 @@
21772284
KR: '🇰🇷 韩国', DE: '🇩🇪 德国', SE: '🇸🇪 瑞典', NL: '🇳🇱 荷兰',
21782285
FI: '🇫🇮 芬兰', GB: '🇬🇧 英国'
21792286
},
2180-
terminal: '终端 v2.9.1',
2287+
terminal: '终端 v2.9.2',
21812288
githubProject: 'GitHub 项目',
21822289
autoDetectClient: '自动识别',
21832290
selectionLogicText: '同地区 → 邻近地区 → 其他地区',
@@ -3024,7 +3131,12 @@
30243131
var finalUrl = subscriptionUrl;
30253132

30263133
if (clientType === atob('djJyYXk=')) {
3027-
finalUrl = subscriptionUrl;
3134+
// 如果是 Shadowrocket,自动添加 target=shadowrocket 参数
3135+
if (clientName === 'Shadowrocket') {
3136+
finalUrl = subscriptionUrl + (subscriptionUrl.includes('?') ? '&' : '?') + 'target=shadowrocket';
3137+
} else {
3138+
finalUrl = subscriptionUrl;
3139+
}
30283140
var urlElement = document.getElementById("clientSubscriptionUrl");
30293141
urlElement.textContent = finalUrl;
30303142
urlElement.style.display = "block";

0 commit comments

Comments
 (0)