-
Notifications
You must be signed in to change notification settings - Fork 7.2k
Expand file tree
/
Copy pathsnippets
More file actions
580 lines (513 loc) · 27.9 KB
/
Copy pathsnippets
File metadata and controls
580 lines (513 loc) · 27.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
import { connect } from 'cloudflare:sockets';
const 基础64文本解码器 = new TextDecoder();
function 解码64(文本) {
const 二进制 = atob(文本);
const 字节 = new Uint8Array(二进制.length);
for (let 索引 = 0; 索引 < 二进制.length; 索引++) 字节[索引] = 二进制.charCodeAt(索引);
return 基础64文本解码器.decode(字节);
}
// --- 硬编码配置 ---
const 认证令牌 = 'f64bdc57-0f54-4705-bf75-cfd646d98c06';
let 回退地址 = '';
const 代理5配置 = '';
// 手动指定地区(留空则自动检测,可选值:US、SG、JP、HK、KR、DE、SE、NL、FI、GB)
const 手动工作器地区 = '';
// D短地址(自定义路径,留空则使用UUID路径,支持多级路径如:mypath 或 path/to/sub)
const 自定义路径 = '';
// GitHub订阅URL(硬编码)
const 仓库优选网址 = 'https://raw.githubusercontent.com/qwer-search/bestip/refs/heads/main/kejilandbestip.txt';
// 启用GitHub优选IP(true启用,false禁用)
const 启用仓库优选 = true;
// 启用其他优选(域名优选,true启用,false禁用)
const 启用其他优选 = true;
// API地址配置(订阅转换服务)
const 订阅转换接口 = 解码64('aHR0cHM6Ly91cmwudjEubWsvc3Vi');
const 直连域名列表 = [
{ name: "cloudflare.182682.xyz", domain: "cloudflare.182682.xyz" },
{ name: "speed.marisalnc.com", domain: "speed.marisalnc.com" },
{ domain: "freeyx.cloudflare88.eu.org" }, { domain: "bestcf.top" },
{ domain: "cdn.2020111.xyz" }, { domain: "cfip.cfcdn.vip" },
{ domain: "cf.0sm.com" }, { domain: "cf.090227.xyz" },
{ domain: "cf.zhetengsha.eu.org" }, { domain: "cloudflare.9jy.cc" },
{ domain: "cf.zerone-cdn.pp.ua" }, { domain: "cfip.1323123.xyz" },
{ domain: "cnamefuckxxs.yuchen.icu" }, { domain: "cloudflare-ip.mofashi.ltd" },
{ domain: "115155.xyz" }, { domain: "cname.xirancdn.us" },
{ domain: "f3058171cad.002404.xyz" }, { domain: "8.889288.xyz" },
{ domain: "cdn.tzpro.xyz" }, { domain: "cf.877771.xyz" },
{ domain: "xn--b6gac.eu.org" }
];
const 已解析代理5配置 = {};
const 是否代理已启用 = false;
let 启用地区匹配 = true;
let 当前工作器地区 = '';
const 备用地址列表 = [
{ domain: 解码64('UHJveHlJUC5VUy5DTUxpdXNzc3MubmV0'), region: 'US', regionCode: 'US', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5TRy5DTUxpdXNzc3MubmV0'), region: 'SG', regionCode: 'SG', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5KUC5DTUxpdXNzc3MubmV0'), region: 'JP', regionCode: 'JP', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5ISy5DTUxpdXNzc3MubmV0'), region: 'HK', regionCode: 'HK', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5LUi5DTUxpdXNzc3MubmV0'), region: 'KR', regionCode: 'KR', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5ERS5DTUxpdXNzc3MubmV0'), region: 'DE', regionCode: 'DE', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5TRS5DTUxpdXNzc3MubmV0'), region: 'SE', regionCode: 'SE', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5OTC5DTUxpdXNzc3MubmV0'), region: 'NL', regionCode: 'NL', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5GSS5DTUxpdXNzc3MubmV0'), region: 'FI', regionCode: 'FI', 端口: 443 },
{ domain: 解码64('UHJveHlJUC5HQi5DTUxpdXNzc3MubmV0'), region: 'GB', regionCode: 'GB', 端口: 443 }
];
const 错误_无效数据 = atob('aW52YWxpZCBkYXRh');
const 错误_无效用户 = atob('aW52YWxpZCB1c2Vy');
const 错误_不支持命令 = atob('Y29tbWFuZCBpcyBub3Qgc3VwcG9ydGVk');
const 错误_仅支持域名系统用户数据报 = atob('VURQIHByb3h5IG9ubHkgZW5hYmxlIGZvciBETlMgd2hpY2ggaXMgcG9ydCA1Mw==');
const 错误_无效地址类型 = atob('aW52YWxpZCBhZGRyZXNzVHlwZQ==');
const 错误_空地址 = atob('YWRkcmVzc1ZhbHVlIGlzIGVtcHR5');
const 错误_网页套接字未打开 = atob('d2ViU29ja2V0LmVhZHlTdGF0ZSBpcyBub3Qgb3Blbg==');
const 错误_无效标识字符串 = atob('U3RyaW5naWZpZWQgaWRlbnRpZmllciBpcyBpbnZhbGlk');
const 错误_无效代理地址 = atob('SW52YWxpZCBTT0NLUyBhZGRyZXNzIGZvcm1hdA==');
const 错误_代理无可用方法 = atob('bm8gYWNjZXB0YWJsZSBtZXRob2Rz');
const 错误_代理需要认证 = atob('c29ja3Mgc2VydmVyIG5lZWRzIGF1dGg=');
const 错误_代理认证失败 = atob('ZmFpbCB0byBhdXRoIHNvY2tzIHNlcnZlcg==');
const 错误_代理连接失败 = atob('ZmFpbCB0byBvcGVuIHNvY2tzIGNvbm5lY3Rpb24=');
const 地址类型_四版 = 1;
const 地址类型_网址 = 2;
const 地址类型_六版 = 3;
async function 检测工作器地区(请求) {
try {
const 云端国家 = 请求.cf?.country;
if (云端国家) {
const 国家地区映射 = {
'US': 'US', 'SG': 'SG', 'JP': 'JP', 'HK': 'HK', 'KR': 'KR',
'DE': 'DE', 'SE': 'SE', 'NL': 'NL', 'FI': 'FI', 'GB': 'GB',
'CN': 'HK', 'TW': 'HK', 'AU': 'SG', 'CA': 'US',
'FR': 'DE', 'IT': 'DE', 'ES': 'DE', 'CH': 'DE',
'AT': 'DE', 'BE': 'NL', 'DK': 'SE', 'NO': 'SE', 'IE': 'GB'
};
if (国家地区映射[云端国家]) return 国家地区映射[云端国家];
}
return 'HK';
} catch (error) {
return 'HK';
}
}
function 获取邻近地区(地区) {
const 邻近映射 = {
'US': ['SG', 'JP', 'HK', 'KR'],
'SG': ['JP', 'HK', 'KR', 'US'],
'JP': ['SG', 'HK', 'KR', 'US'],
'HK': ['SG', 'JP', 'KR', 'US'],
'KR': ['JP', 'HK', 'SG', 'US'],
'DE': ['NL', 'GB', 'SE', 'FI'],
'SE': ['DE', 'NL', 'FI', 'GB'],
'NL': ['DE', 'GB', 'SE', 'FI'],
'FI': ['SE', 'DE', 'NL', 'GB'],
'GB': ['DE', 'NL', 'SE', 'FI']
};
return 邻近映射[地区] || [];
}
function 获取优先地区列表(地区) {
const 邻近地区列表 = 获取邻近地区(地区);
const 全部地区列表 = ['US', 'SG', 'JP', 'HK', 'KR', 'DE', 'SE', 'NL', 'FI', 'GB'];
return [地区, ...邻近地区列表, ...全部地区列表.filter(r => r !== 地区 && !邻近地区列表.includes(r))];
}
function 获取智能地区选择(工作器地区, 可用地址列表) {
if (!启用地区匹配 || !工作器地区) return 可用地址列表;
const 优先地区列表 = 获取优先地区列表(工作器地区);
const 排序地址列表 = [];
for (const 地区 of 优先地区列表) {
const 地区地址列表 = 可用地址列表.filter(地址项 => 地址项.regionCode === 地区);
排序地址列表.push(...地区地址列表);
}
return 排序地址列表;
}
async function 获取最佳备用地址(工作器地区 = '') {
if (备用地址列表.length === 0) return null;
const 可用地址列表 = 备用地址列表.map(地址项 => ({ ...地址项, available: true }));
if (启用地区匹配 && 工作器地区) {
const 排序地址列表 = 获取智能地区选择(工作器地区, 可用地址列表);
if (排序地址列表.length > 0) return 排序地址列表[0];
}
return 可用地址列表[0];
}
function 解析地址和端口(输入) {
if (!输入) return { 地址: '', 端口: null };
if (输入.includes('[') && 输入.includes(']')) {
const 匹配 = 输入.match(/^\[([^\]]+)\](?::(\d+))?$/);
if (匹配) {
return { 地址: 匹配[1], 端口: 匹配[2] ? parseInt(匹配[2], 10) : null };
}
}
const 最后冒号索引 = 输入.lastIndexOf(':');
if (最后冒号索引 > 0) {
const 地址 = 输入.substring(0, 最后冒号索引);
const 端口文本 = 输入.substring(最后冒号索引 + 1);
const 端口 = parseInt(端口文本, 10);
if (!isNaN(端口) && 端口 > 0 && 端口 <= 65535) return { 地址, 端口 };
}
return { 地址: 输入, 端口: null };
}
export default {
async fetch(请求, 环境, 上下文) {
try {
const 网址 = new URL(请求.url);
if (手动工作器地区 && 手动工作器地区.trim()) {
当前工作器地区 = 手动工作器地区.trim().toUpperCase();
} else {
当前工作器地区 = await 检测工作器地区(请求);
}
let 当前回退地址 = 回退地址;
if (!当前回退地址 && 当前工作器地区) {
const 最佳备用地址 = await 获取最佳备用地址(当前工作器地区);
if (最佳备用地址) 当前回退地址 = 最佳备用地址.domain + ':' + 最佳备用地址.端口;
}
if (请求.headers.get('Upgrade') === 'websocket') {
return await 处理网页套接字请求(请求, 当前回退地址);
} else if (请求.method === 'GET') {
if (网址.pathname === '/') {
const 成功页面 = `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>服务正常</title><style>body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background-color:#121212;color:#e0e0e0;text-align:center;}.container{padding:2rem;border-radius:8px;background-color:#1e1e1e;box-shadow:0 4px 6px rgba(0,0,0,0.1);}h1{color:#4caf50;}</style></head><body><div class="container"><h1>✅ 服务正常</h1><p>请继续后面的操作。</p></div></body></html>`;
return new Response(成功页面, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' } });
}
if (自定义路径 && 自定义路径.trim()) {
const 整理自定义路径 = 自定义路径.trim().startsWith('/') ? 自定义路径.trim() : '/' + 自定义路径.trim();
const 规范自定义路径 = 整理自定义路径.endsWith('/') && 整理自定义路径.length > 1 ? 整理自定义路径.slice(0, -1) : 整理自定义路径;
const 规范路径 = 网址.pathname.endsWith('/') && 网址.pathname.length > 1 ? 网址.pathname.slice(0, -1) : 网址.pathname;
if (规范路径 === 规范自定义路径) {
return await 处理订阅页面(请求, 认证令牌);
}
if (规范路径 === 规范自定义路径 + '/sub') {
return await 处理订阅请求(请求, 认证令牌, 网址);
}
if (网址.pathname.length > 1 && 网址.pathname !== '/') {
const 用户 = 网址.pathname.replace(/\/$/, '').replace('/sub', '').substring(1);
if (是否有效格式(用户)) {
return new Response(JSON.stringify({
error: '访问被拒绝',
message: '当前 Worker 已启用自定义路径模式,UUID 访问已禁用'
}), {
status: 403,
headers: { 'Content-Type': 'application/json; charset=utf-8' }
});
}
}
} else {
if (网址.pathname.length > 1 && 网址.pathname !== '/' && !网址.pathname.includes('/sub')) {
const 唯一标识 = 网址.pathname.replace(/\/$/, '').substring(1);
if (是否有效格式(唯一标识)) {
if (唯一标识 === 认证令牌) return await 处理订阅页面(请求, 唯一标识);
return new Response('UUID错误', { status: 403 });
}
}
if (网址.pathname.includes('/sub')) {
const 路径部分 = 网址.pathname.split('/');
if (路径部分.length === 2 && 路径部分[1] === 'sub') {
const 唯一标识 = 路径部分[0].substring(1);
if (是否有效格式(唯一标识)) {
if (唯一标识 === 认证令牌) return await 处理订阅请求(请求, 唯一标识, 网址);
return new Response('UUID错误', { status: 403 });
}
}
}
if (网址.pathname.toLowerCase().includes(`/${认证令牌}`)) {
return await 处理订阅请求(请求, 认证令牌);
}
}
}
return new Response('Not Found', { status: 404 });
} catch (err) {
return new Response(err.toString(), { status: 500 });
}
},
};
async function 处理订阅页面(请求, 唯一标识 = null) {
if (!唯一标识) 唯一标识 = 认证令牌;
const 页面文本 = `<!doctype html><html lang=zh-CN><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>订阅中心</title><style>body{margin:0;background:#0b0f14;color:#e8eef6;font:15px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.w{max-width:760px;margin:auto;padding:28px}h1{font-size:26px;margin:0 0 10px}.g{display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:8px;margin:18px 0}button{border:1px solid #2f4054;background:#121b26;color:#e8eef6;border-radius:6px;padding:10px;cursor:pointer}button:hover{background:#1a2635}.o{display:none;white-space:pre-wrap;word-break:break-all;background:#101820;border:1px solid #2a3848;border-radius:6px;padding:12px;margin-top:14px}a{color:#7cc7ff}</style><div class=w><h1>订阅中心</h1><p>选择客户端生成链接,或直接获取订阅内容。</p><div id=b class=g></div><button onclick="b64()">获取订阅内容</button><p><a href=https://github.com/byJoey/cfnew target=_blank>GitHub</a> · <a href=https://www.youtube.com/@joeyblog target=_blank>YouTube</a></p><div id=o class=o></div></div><script>const S="${订阅转换接口}",d=s=>atob(s),B=[{n:d('Q0xBU0g='),sub:1,t:d('Y2xhc2g='),s:d('Y2xhc2g6Ly9pbnN0YWxsLWNvbmZpZz91cmw9')},{n:d('U1VSR0U='),sub:1,t:d('c3VyZ2U='),s:d('c3VyZ2U6Ly8vaW5zdGFsbC1jb25maWc/dXJsPQ==')},{n:d('U0lORy1CT1g='),sub:1,t:d('c2luZ2JveA=='),s:d('c2luZy1ib3g6Ly9pbnN0YWxsLWNvbmZpZz91cmw9')},{n:d('TE9PTg=='),sub:1,t:d('bG9vbg=='),s:d('bG9vbjovL2luc3RhbGw/dXJsPQ==')},{n:d('UVVBTlRVTVVMVCBY'),sub:1,t:d('cXVhbng='),s:d('cXVhbnR1bXVsdC14Oi8vaW5zdGFsbC1jb25maWc/dXJsPQ==')},{n:d('VjJSQVk='),sub:0,t:'',s:''},{n:d('U2hhZG93cm9ja2V0'),sub:0,t:'',s:d('c2hhZG93cm9ja2V0Oi8vYWRkLw==')},{n:d('VjJSQVlORw=='),sub:0,t:'',s:d('djJyYXluZzovL2luc3RhbGw/dXJsPQ==')},{n:d('TkVLT1JBWQ=='),sub:0,t:'',s:d('bmVrb3JheTovL2luc3RhbGwtY29uZmlnP3VybD0=')},{n:d('U1RBU0g='),sub:1,t:d('Y2xhc2g='),s:d('c3Rhc2g6Ly9pbnN0YWxsP3VybD0=')}],O=document.getElementById("o");b.innerHTML=B.map((x,i)=>'<button onclick=g('+i+')>'+x.n+'</button>').join("");function show(t){O.style.display="block";O.textContent=t;navigator.clipboard&&navigator.clipboard.writeText(t).catch(()=>{})}function app(s){if(!s)return;let i=document.createElement("iframe");i.style.display="none";i.src=s;document.body.append(i);setTimeout(()=>i.remove(),1200)}function g(i){let c=B[i],u=location.href.replace(/\\/$/,"")+"/sub",f=u;if(c.sub){f=S+"?target="+c.t+"&url="+encodeURIComponent(u)+"&insert=false"}let s=c.s?c.s+encodeURIComponent(f):"";show(f);app(s)}function b64(){fetch(location.href.replace(/\\/$/,"")+"/sub").then(r=>r.text()).then(show).catch(()=>show("获取订阅失败"))}</script></html>`;
return new Response(页面文本, {
status: 200,
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
}
async function 处理订阅请求(请求, 唯一标识, 网址 = null) {
if (!网址) 网址 = new URL(请求.url);
const 最终链接列表 = [];
const 工作器域名 = 网址.hostname;
const 原生列表 = [{ ip: 工作器域名, isp: '原生地址' }];
最终链接列表.push(...从来源生成链接(原生列表, 唯一标识, 工作器域名));
if (启用其他优选) {
const 域名列表 = 直连域名列表.map(d => ({ ip: d.domain, isp: d.name || d.domain }));
最终链接列表.push(...从来源生成链接(域名列表, 唯一标识, 工作器域名));
}
if (启用仓库优选) {
const 新地址列表 = await 拉取并解析新地址();
if (新地址列表.length > 0) 最终链接列表.push(...从新地址生成链接(新地址列表, 唯一标识, 工作器域名));
}
if (最终链接列表.length === 0) {
const 错误备注 = "所有节点获取失败";
const 错误链接 = `${解码64('dmxlc3M=')}://00000000-0000-0000-0000-000000000000@127.0.0.1:80?encryption=none&security=none&type=ws&host=error.com&path=%2F#${encodeURIComponent(错误备注)}`;
最终链接列表.push(错误链接);
}
const 订阅内容 = btoa(最终链接列表.join('\n'));
return new Response(订阅内容, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
},
});
}
function 从来源生成链接(列表, 唯一标识, 工作器域名) {
const 传输层安全端口列表 = [443];
const 链接列表 = [];
const 网页套接字路径 = '/?ed=2048';
const 协议 = 解码64('dmxlc3M=');
列表.forEach(项 => {
const 节点名基础 = 项.isp.replace(/\s/g, '_');
const 安全地址 = 项.ip.includes(':') ? `[${项.ip}]` : 项.ip;
传输层安全端口列表.forEach(端口 => {
const 网页套接字节点名 = `${节点名基础}-${端口}-WS-TLS`;
const 网页套接字参数 = new URLSearchParams({
encryption: 'none',
security: 'tls',
sni: 工作器域名,
fp: 'randomized',
type: 'ws',
host: 工作器域名,
path: 网页套接字路径
});
链接列表.push(`${协议}://${唯一标识}@${安全地址}:${端口}?${网页套接字参数.toString()}#${encodeURIComponent(网页套接字节点名)}`);
});
});
return 链接列表;
}
async function 拉取并解析新地址() {
const 网址 = 仓库优选网址;
try {
const 响应 = await fetch(网址);
if (!响应.ok) return [];
const 文本 = await 响应.text();
const 结果列表 = [];
const 行列表 = 文本.trim().replace(/\r/g, "").split('\n');
const 正则 = /^([^:]+):(\d+)#(.*)$/;
for (const 行 of 行列表) {
const 整理行 = 行.trim();
if (!整理行) continue;
const 匹配 = 整理行.match(正则);
if (匹配) {
结果列表.push({
ip: 匹配[1],
端口: parseInt(匹配[2], 10),
name: 匹配[3].trim() || 匹配[1]
});
}
}
return 结果列表;
} catch (error) {
return [];
}
}
function 从新地址生成链接(列表, 唯一标识, 工作器域名) {
const 链接列表 = [];
const 网页套接字路径 = '/?ed=2048';
const 协议 = 解码64('dmxlc3M=');
列表.forEach(项 => {
const 节点名 = 项.name;
const 安全地址 = 项.ip.includes(':') ? `[${项.ip}]` : 项.ip;
const 参数 = {
encryption: 'none',
security: 'tls',
sni: 工作器域名,
fp: 'randomized',
type: 'ws',
host: 工作器域名,
path: 网页套接字路径
};
const 网页套接字参数 = new URLSearchParams(参数);
链接列表.push(`${协议}://${唯一标识}@${安全地址}:${项.端口}?${网页套接字参数.toString()}#${encodeURIComponent(节点名)}`);
});
return 链接列表;
}
async function 处理网页套接字请求(请求, 当前回退地址 = null) {
const 网页套接字对 = new WebSocketPair();
const [客户端套接字, 服务端套接字] = Object.values(网页套接字对);
服务端套接字.accept();
let 远程连接包装 = { 套接字: null };
let 是否域名系统查询 = false;
const 回退地址值 = 当前回退地址 || 回退地址;
const 早期数据 = 请求.headers.get('sec-websocket-protocol') || '';
const 可读流 = 创建可读流(服务端套接字, 早期数据);
可读流.pipeTo(new WritableStream({
async write(数据块) {
if (是否域名系统查询) return await 转发用户数据报(数据块, 服务端套接字, null);
if (远程连接包装.套接字) {
const 写入器 = 远程连接包装.套接字.writable.getWriter();
await 写入器.write(数据块);
写入器.releaseLock();
return;
}
const { 是否有错误, 消息, 地址类型, 端口, 主机名, 原始索引, 版本, 是否用户数据报 } = 解析套接字包头(数据块, 认证令牌);
if (是否有错误) throw new Error(消息);
if (是否用户数据报) {
if (端口 === 53) 是否域名系统查询 = true;
else throw new Error(错误_仅支持域名系统用户数据报);
}
const 响应头 = new Uint8Array([版本[0], 0]);
const 原始数据 = 数据块.slice(原始索引);
if (是否域名系统查询) return 转发用户数据报(原始数据, 服务端套接字, 响应头);
await 转发传输控制(地址类型, 主机名, 端口, 原始数据, 服务端套接字, 响应头, 远程连接包装, 回退地址值);
},
})).catch((err) => { console.log('WS Stream Error:', err); });
return new Response(null, { status: 101, webSocket: 客户端套接字 });
}
async function 转发传输控制(地址类型参数, 主机, 端口数字, 原始数据, 网页套接字, 响应头, 远程连接包装, 回退地址值 = null) {
async function 连接并发送(地址, 端口) {
const 远程套接字 = connect({ hostname: 地址, port: 端口 });
const 写入器 = 远程套接字.writable.getWriter();
await 写入器.write(原始数据);
写入器.releaseLock();
return 远程套接字;
}
async function 重试连接() {
let 回退主机, 回退端口;
if (回退地址值 && 回退地址值.trim()) {
const 解析值 = 解析地址和端口(回退地址值);
回退主机 = 解析值.地址;
回退端口 = 解析值.端口 || 端口数字;
} else if (回退地址 && 回退地址.trim()) {
const 解析值 = 解析地址和端口(回退地址);
回退主机 = 解析值.地址;
回退端口 = 解析值.端口 || 端口数字;
} else {
const 最佳备用地址 = await 获取最佳备用地址(当前工作器地区);
回退主机 = 最佳备用地址 ? 最佳备用地址.domain : 主机;
回退端口 = 最佳备用地址 ? 最佳备用地址.端口 : 端口数字;
}
const 新套接字 = await 连接并发送(回退主机 || 主机, 回退端口);
远程连接包装.套接字 = 新套接字;
新套接字.closed.catch(() => { }).finally(() => 安静关闭套接字(网页套接字));
连接流(新套接字, 网页套接字, 响应头, null);
}
try {
const 初始套接字 = await 连接并发送(主机, 端口数字);
远程连接包装.套接字 = 初始套接字;
连接流(初始套接字, 网页套接字, 响应头, 重试连接);
} catch (err) {
console.log('Initial connection failed, trying fallback:', err);
重试连接();
}
}
function 解析套接字包头(数据块, 令牌) {
if (数据块.byteLength < 24) return { 是否有错误: true, 消息: 错误_无效数据 };
const 版本 = new Uint8Array(数据块.slice(0, 1));
if (格式化标识(new Uint8Array(数据块.slice(1, 17))) !== 令牌) return { 是否有错误: true, 消息: 错误_无效用户 };
const 选项长度 = new Uint8Array(数据块.slice(17, 18))[0];
const 命令 = new Uint8Array(数据块.slice(18 + 选项长度, 19 + 选项长度))[0];
let 是否用户数据报 = false;
if (命令 === 1) { }
else if (命令 === 2) { 是否用户数据报 = true; }
else { return { 是否有错误: true, 消息: 错误_不支持命令 }; }
const 端口索引 = 19 + 选项长度;
const 端口 = new DataView(数据块.slice(端口索引, 端口索引 + 2)).getUint16(0);
let 地址索引 = 端口索引 + 2, 地址长度 = 0, 地址值索引 = 地址索引 + 1, 主机名 = '';
const 地址类型 = new Uint8Array(数据块.slice(地址索引, 地址值索引))[0];
switch (地址类型) {
case 地址类型_四版:
地址长度 = 4;
主机名 = new Uint8Array(数据块.slice(地址值索引, 地址值索引 + 地址长度)).join('.');
break;
case 地址类型_网址:
地址长度 = new Uint8Array(数据块.slice(地址值索引, 地址值索引 + 1))[0];
地址值索引 += 1;
主机名 = new TextDecoder().decode(数据块.slice(地址值索引, 地址值索引 + 地址长度));
break;
case 地址类型_六版:
地址长度 = 16;
const 六版地址段 = [];
const 六版视图 = new DataView(数据块.slice(地址值索引, 地址值索引 + 地址长度));
for (let i = 0; i < 8; i++) 六版地址段.push(六版视图.getUint16(i * 2).toString(16));
主机名 = 六版地址段.join(':');
break;
default:
return { 是否有错误: true, 消息: `${错误_无效地址类型}: ${地址类型}` };
}
if (!主机名) return { 是否有错误: true, 消息: `${错误_空地址}: ${地址类型}` };
return { 是否有错误: false, 地址类型, 端口, 主机名, 是否用户数据报, 原始索引: 地址值索引 + 地址长度, 版本 };
}
function 创建可读流(套接字, 早期数据头) {
let 已取消 = false;
return new ReadableStream({
start(控制器) {
套接字.addEventListener('message', (事件) => { if (!已取消) 控制器.enqueue(事件.data); });
套接字.addEventListener('close', () => { if (!已取消) { 安静关闭套接字(套接字); 控制器.close(); } });
套接字.addEventListener('error', (err) => 控制器.error(err));
const { 早期数据, error } = 基础64转数组(早期数据头);
if (error) 控制器.error(error);
else if (早期数据) 控制器.enqueue(早期数据);
},
cancel() { 已取消 = true; 安静关闭套接字(套接字); }
});
}
async function 连接流(远程套接字, 网页套接字, 头数据, 重试函数) {
let 头 = 头数据, 是否有数据 = false;
await 远程套接字.readable.pipeTo(
new WritableStream({
async write(数据块, 控制器) {
是否有数据 = true;
if (网页套接字.readyState !== 1) 控制器.error(错误_网页套接字未打开);
if (头) {
网页套接字.send(await new Blob([头, 数据块]).arrayBuffer());
头 = null;
} else {
网页套接字.send(数据块);
}
},
abort(原因) { console.error("Readable aborted:", 原因); },
})
).catch((error) => { console.error("Stream connection error:", error); 安静关闭套接字(网页套接字); });
if (!是否有数据 && 重试函数) 重试函数();
}
async function 转发用户数据报(用户数据报块, 网页套接字, 响应头) {
try {
const 传输控制套接字 = connect({ hostname: '8.8.4.4', port: 53 });
let 协议头 = 响应头;
const 写入器 = 传输控制套接字.writable.getWriter();
await 写入器.write(用户数据报块);
写入器.releaseLock();
await 传输控制套接字.readable.pipeTo(new WritableStream({
async write(数据块) {
if (网页套接字.readyState === 1) {
if (协议头) {
网页套接字.send(await new Blob([协议头, 数据块]).arrayBuffer());
协议头 = null;
} else {
网页套接字.send(数据块);
}
}
},
}));
} catch (error) {
console.error(`DNS forward error: ${error.message}`);
}
}
function 基础64转数组(基础64文本) {
if (!基础64文本) return { error: null };
try {
基础64文本 = 基础64文本.replace(/-/g, '+').replace(/_/g, '/');
return { 早期数据: Uint8Array.from(atob(基础64文本), (c) => c.charCodeAt(0)).buffer, error: null };
} catch (error) {
return { error };
}
}
function 是否有效格式(唯一标识) {
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(唯一标识);
}
function 安静关闭套接字(套接字) {
try { if (套接字.readyState === 1 || 套接字.readyState === 2) 套接字.close(); }
catch (error) { }
}
const 十六进制表 = Array.from({ length: 256 }, (v, i) => (i + 256).toString(16).slice(1));
function 格式化标识(数组, 偏移 = 0) {
const 标识 = (
十六进制表[数组[偏移]] + 十六进制表[数组[偏移 + 1]] + 十六进制表[数组[偏移 + 2]] + 十六进制表[数组[偏移 + 3]] + "-" +
十六进制表[数组[偏移 + 4]] + 十六进制表[数组[偏移 + 5]] + "-" +
十六进制表[数组[偏移 + 6]] + 十六进制表[数组[偏移 + 7]] + "-" +
十六进制表[数组[偏移 + 8]] + 十六进制表[数组[偏移 + 9]] + "-" +
十六进制表[数组[偏移 + 10]] + 十六进制表[数组[偏移 + 11]] + 十六进制表[数组[偏移 + 12]] + 十六进制表[数组[偏移 + 13]] +
十六进制表[数组[偏移 + 14]] + 十六进制表[数组[偏移 + 15]]
).toLowerCase();
if (!是否有效格式(标识)) throw new TypeError(错误_无效标识字符串);
return 标识;
}