js-tcp-tunnel 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/lib.js +1435 -1542
- package/src/lib.test.js +3 -2
- package/src/types.ts +114 -0
package/src/lib.js
CHANGED
|
@@ -1,1543 +1,1436 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto'
|
|
2
|
-
import net from 'node:net'
|
|
3
|
-
import { Readable, Writable } from 'node:stream'
|
|
4
|
-
import http from 'node:http'
|
|
5
|
-
import https from 'node:https'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* @import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export const
|
|
21
|
-
export const
|
|
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
|
-
if (offset +
|
|
151
|
-
remain = buffer.subarray(offset)
|
|
152
|
-
break
|
|
153
|
-
}
|
|
154
|
-
let
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
break
|
|
159
|
-
}
|
|
160
|
-
let
|
|
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
|
-
if (encoding == '
|
|
221
|
-
array = new
|
|
222
|
-
}
|
|
223
|
-
if (
|
|
224
|
-
array =
|
|
225
|
-
}
|
|
226
|
-
if (
|
|
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
|
-
let
|
|
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
|
-
"PBKDF2",
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const
|
|
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
|
-
export const
|
|
420
|
-
export const
|
|
421
|
-
export const
|
|
422
|
-
export const
|
|
423
|
-
export const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
export
|
|
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
|
-
let
|
|
533
|
-
let
|
|
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
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
}
|
|
761
|
-
if (data.type ==
|
|
762
|
-
|
|
763
|
-
let
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
channelMap.
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
let
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
if (
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
if (
|
|
853
|
-
let
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
*/
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
async function
|
|
1109
|
-
let
|
|
1110
|
-
let
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
*
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
})
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
})
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
let
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
})
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
await
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
* @
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
*
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
await
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
param.
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
* @returns {http}
|
|
1437
|
-
*/
|
|
1438
|
-
export function getHttpx(url) {
|
|
1439
|
-
/** @type{*} */
|
|
1440
|
-
let httpx = http
|
|
1441
|
-
if (url.startsWith('https:')) {
|
|
1442
|
-
httpx = https
|
|
1443
|
-
}
|
|
1444
|
-
return httpx
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
/**
|
|
1448
|
-
* @param {{
|
|
1449
|
-
* signal:AbortSignal;
|
|
1450
|
-
* serverKey?:string;
|
|
1451
|
-
* url:string;
|
|
1452
|
-
* timeout?:number;
|
|
1453
|
-
* oncreateoutconnect?:()=>{};
|
|
1454
|
-
* headersFn?:()=>Promise<object>;
|
|
1455
|
-
* }} param
|
|
1456
|
-
*/
|
|
1457
|
-
export function createTunnelTcpClientHttp(param) {
|
|
1458
|
-
let helper = createTunnelTcpClientHelper({
|
|
1459
|
-
serverKey: param.serverKey,
|
|
1460
|
-
signal: param.signal,
|
|
1461
|
-
uniqueId: 1,
|
|
1462
|
-
clientDataId: 0,
|
|
1463
|
-
})
|
|
1464
|
-
helper.writer = helper.writable.getWriter()
|
|
1465
|
-
let signal = Promise_withResolvers()
|
|
1466
|
-
/** @type{WritableStreamDefaultWriter<Uint8Array>} */
|
|
1467
|
-
let socketWriter = null
|
|
1468
|
-
let bufferedTransform = createTimeBufferedTransformStream(50)
|
|
1469
|
-
helper.readable.pipeThrough(bufferedTransform).pipeTo(new WritableStream({
|
|
1470
|
-
async write(chunk) {
|
|
1471
|
-
while (!param.signal.aborted && socketWriter == null) {
|
|
1472
|
-
await signal.promise
|
|
1473
|
-
}
|
|
1474
|
-
if (!param.signal.aborted) {
|
|
1475
|
-
await socketWriter.write(chunk)
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
})).catch((err) => { console.error('web stream error', err.message) })
|
|
1479
|
-
|
|
1480
|
-
/** @type{Set<()=>void>} */
|
|
1481
|
-
const abortListenerSet = new Set()
|
|
1482
|
-
param.signal.addEventListener('abort', () => {
|
|
1483
|
-
abortListenerSet.forEach(o => o())
|
|
1484
|
-
})
|
|
1485
|
-
async function createConnectionHttpx() {
|
|
1486
|
-
console.info('createTunnelTcpClientHttpV2 createConnectionHttpx')
|
|
1487
|
-
let addHeaders = {}
|
|
1488
|
-
if (param.headersFn) {
|
|
1489
|
-
addHeaders = await param.headersFn()
|
|
1490
|
-
}
|
|
1491
|
-
const ac = new AbortController()
|
|
1492
|
-
const listenerAC = () => { ac.abort() }
|
|
1493
|
-
let promise = Promise_withResolvers()
|
|
1494
|
-
let transform = new TransformStream()
|
|
1495
|
-
socketWriter = transform.writable.getWriter()
|
|
1496
|
-
signal.resolve()
|
|
1497
|
-
abortListenerSet.add(listenerAC)
|
|
1498
|
-
let httpx = getHttpx(param.url)
|
|
1499
|
-
let req = httpx.request(param.url, {
|
|
1500
|
-
method: 'POST',
|
|
1501
|
-
signal: ac.signal,
|
|
1502
|
-
timeout: param.timeout ?? 24 * 3600 * 1000,
|
|
1503
|
-
}, (res) => {
|
|
1504
|
-
param.oncreateoutconnect && param.oncreateoutconnect()
|
|
1505
|
-
Readable.toWeb(res).pipeTo(new WritableStream({
|
|
1506
|
-
async write(chunk) {
|
|
1507
|
-
await helper.writer.write(chunk)
|
|
1508
|
-
}
|
|
1509
|
-
})).catch((err) => {
|
|
1510
|
-
console.error('web stream error', err.message)
|
|
1511
|
-
})
|
|
1512
|
-
res.on('error', (e) => {
|
|
1513
|
-
console.error('createConnectionHttpx res error ', e.message)
|
|
1514
|
-
promise.resolve()
|
|
1515
|
-
})
|
|
1516
|
-
res.on('close', () => {
|
|
1517
|
-
console.error('createConnectionHttpx res close ')
|
|
1518
|
-
promise.resolve()
|
|
1519
|
-
})
|
|
1520
|
-
})
|
|
1521
|
-
Readable.fromWeb(transform.readable).pipe(req)
|
|
1522
|
-
req.flushHeaders()
|
|
1523
|
-
req.on('error', (e) => {
|
|
1524
|
-
console.error('createConnectionHttpx', e.message)
|
|
1525
|
-
promise.resolve()
|
|
1526
|
-
})
|
|
1527
|
-
req.on('close', () => {
|
|
1528
|
-
console.error('createConnectionHttpx req close')
|
|
1529
|
-
promise.resolve()
|
|
1530
|
-
})
|
|
1531
|
-
await promise.promise
|
|
1532
|
-
abortListenerSet.delete(listenerAC)
|
|
1533
|
-
socketWriter = null
|
|
1534
|
-
signal.resolve()
|
|
1535
|
-
signal = Promise_withResolvers()
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
timeWaitRetryLoop(param.signal, async () => {
|
|
1539
|
-
await createConnectionHttpx()
|
|
1540
|
-
})
|
|
1541
|
-
|
|
1542
|
-
return helper
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import net from 'node:net'
|
|
3
|
+
import { Readable, Writable } from 'node:stream'
|
|
4
|
+
import http from 'node:http'
|
|
5
|
+
import https from 'node:https'
|
|
6
|
+
import { ReadableStream, TransformStream, WritableStream } from 'node:stream/web'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @import {WebSocketServer} from 'ws'
|
|
10
|
+
* @import Router from 'koa-router'
|
|
11
|
+
* @import {SocketChannel, TCP_TUNNEL_DATA, TUNNEL_TCP_CLIENT_HELPER, TUNNEL_TCP_DATA_CONNECT, TUNNEL_TCP_DATA_LISTEN, TUNNEL_TCP_DATA_PINGPONG, TUNNEL_TCP_SERVER_HELPER, TunnelTcpClientHelperParam, TunnelTcpServerHelperParam} from './types.js'
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const DEBUG_TUNNEL_TCP = false
|
|
15
|
+
const TUNNEL_TCP_WITH_CRYPTO = true
|
|
16
|
+
const TUNNEL_TCP_TIME_BUFFERED = false
|
|
17
|
+
// const TUNNEL_TCP_QUEUE_SIZE = 15
|
|
18
|
+
// const TUNNEL_TCP_ACK_SIZE = 10
|
|
19
|
+
|
|
20
|
+
export const md5 = (/**@type{string}*/s) => createHash("md5").update(s).digest('hex')
|
|
21
|
+
export const sleep = (/** @type {number} */ timeout) => new Promise((resolve) => setTimeout(resolve, timeout))
|
|
22
|
+
export const sha256 = (/**@type{string}*/s) => createHash("sha256").update(s).digest('hex')
|
|
23
|
+
export const sha512 = (/**@type{string}*/s) => createHash("sha512").update(s).digest('hex')
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {(ac: AbortController) => Promise<void>} func
|
|
27
|
+
*/
|
|
28
|
+
export async function runWithAbortController(func) {
|
|
29
|
+
let ac = new AbortController()
|
|
30
|
+
try {
|
|
31
|
+
await func(ac)
|
|
32
|
+
await sleep(1000)
|
|
33
|
+
} finally { ac.abort() }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {number} size
|
|
38
|
+
*/
|
|
39
|
+
export function formatSize(size) {
|
|
40
|
+
if (typeof size !== 'number') return ''
|
|
41
|
+
if (size <= 0) { return '0B'.padStart(8, ' ') }
|
|
42
|
+
let companys = 'B KB MB GB TB'.split(' ')
|
|
43
|
+
let cur = size
|
|
44
|
+
while (cur >= 1024) {
|
|
45
|
+
companys.shift()
|
|
46
|
+
cur /= 1024
|
|
47
|
+
}
|
|
48
|
+
return `${formatNumber(cur)}${companys[0]}`.padStart(8, ' ')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {number} num
|
|
53
|
+
*/
|
|
54
|
+
export function formatNumber(num) {
|
|
55
|
+
return Number(num.toFixed(2))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function Promise_withResolvers() {
|
|
59
|
+
/** @type{(value?:object)=>void} */
|
|
60
|
+
let resolve = null
|
|
61
|
+
/** @type{(reason?:object)=>void} */
|
|
62
|
+
let reject = null
|
|
63
|
+
const promise = new Promise((res, rej) => {
|
|
64
|
+
resolve = res
|
|
65
|
+
reject = rej
|
|
66
|
+
})
|
|
67
|
+
return { promise, resolve, reject }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {Promise<[CryptoKey,Uint8Array]>} key_iv
|
|
72
|
+
* @returns {TransformStream<Uint8Array, Uint8Array>}
|
|
73
|
+
*/
|
|
74
|
+
export function createEncodeStream(key_iv) {
|
|
75
|
+
let key = null
|
|
76
|
+
let iv = null
|
|
77
|
+
return new TransformStream({
|
|
78
|
+
async start() {
|
|
79
|
+
[key, iv] = await key_iv
|
|
80
|
+
},
|
|
81
|
+
async transform(chunk, controller) {
|
|
82
|
+
let buffer = await buildBufferData([chunk], key, iv)
|
|
83
|
+
controller.enqueue(buffer)
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {Promise<[CryptoKey,Uint8Array]>} key_iv
|
|
90
|
+
* @returns {TransformStream<Uint8Array, Uint8Array>}
|
|
91
|
+
*/
|
|
92
|
+
export function createDecodeStream(key_iv) {
|
|
93
|
+
let key = null
|
|
94
|
+
let iv = null
|
|
95
|
+
let last = new Uint8Array(0)
|
|
96
|
+
return new TransformStream({
|
|
97
|
+
async start() {
|
|
98
|
+
[key, iv] = await key_iv
|
|
99
|
+
},
|
|
100
|
+
async transform(chunk, controller) {
|
|
101
|
+
let [queueReceive, remain] = await parseBufferData(Uint8Array_concat([last, chunk]), key, iv)
|
|
102
|
+
last = remain
|
|
103
|
+
for (const o of queueReceive) {
|
|
104
|
+
controller.enqueue(o)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const HEADER_CHECK = 0xb1f7705f
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {Uint8Array[]} queue
|
|
114
|
+
* @param {CryptoKey} key
|
|
115
|
+
* @param {Uint8Array} iv
|
|
116
|
+
* @returns {Promise<Uint8Array>}
|
|
117
|
+
*/
|
|
118
|
+
export async function buildBufferData(queue, key, iv) {
|
|
119
|
+
let buffers = []
|
|
120
|
+
for (const data of queue) {
|
|
121
|
+
let offset = 0
|
|
122
|
+
let header = new Uint8Array(8)
|
|
123
|
+
let headerCheck = HEADER_CHECK
|
|
124
|
+
let buffer = await encrypt(data, key, iv)
|
|
125
|
+
writeUInt32LE(header, buffer.length, offset); offset += 4
|
|
126
|
+
writeUInt32LE(header, headerCheck, offset); offset += 4
|
|
127
|
+
buffers.push(header, buffer)
|
|
128
|
+
}
|
|
129
|
+
return Uint8Array_concat(buffers)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
134
|
+
* @param {CryptoKey} key
|
|
135
|
+
* @param {Uint8Array} iv
|
|
136
|
+
* @returns {Promise<[Uint8Array[],Uint8Array<ArrayBuffer>]>}
|
|
137
|
+
*/
|
|
138
|
+
export async function parseBufferData(buffer, key, iv) {
|
|
139
|
+
/** @type{Uint8Array[]} */
|
|
140
|
+
let queue = []
|
|
141
|
+
let offset = 0
|
|
142
|
+
let remain = new Uint8Array(0)
|
|
143
|
+
while (offset < buffer.length) {
|
|
144
|
+
if (offset + 8 > buffer.length) {
|
|
145
|
+
remain = buffer.subarray(offset)
|
|
146
|
+
break
|
|
147
|
+
}
|
|
148
|
+
let bufferLength = readUInt32LE(buffer, offset); offset += 4
|
|
149
|
+
let headerCheck = readUInt32LE(buffer, offset); offset += 4
|
|
150
|
+
if (offset + bufferLength > buffer.length) {
|
|
151
|
+
remain = buffer.subarray(offset - 8)
|
|
152
|
+
break
|
|
153
|
+
}
|
|
154
|
+
let check = HEADER_CHECK
|
|
155
|
+
if (check !== headerCheck) {
|
|
156
|
+
remain = new Uint8Array(0)
|
|
157
|
+
console.error('data check error!', bufferLength, check.toString(16), headerCheck.toString(16))
|
|
158
|
+
break
|
|
159
|
+
}
|
|
160
|
+
let data = buffer.subarray(offset, offset + bufferLength); offset += bufferLength
|
|
161
|
+
let buf = await decrypt(data, key, iv)
|
|
162
|
+
if (buf.length > 0) {
|
|
163
|
+
queue.push(buf)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return [queue, remain]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {Uint8Array} buffer
|
|
172
|
+
* @param {number} offset
|
|
173
|
+
*/
|
|
174
|
+
export function readUInt32LE(buffer, offset) {
|
|
175
|
+
if (offset < 0 || offset + 4 > buffer.length) throw new RangeError('Reading out of bounds')
|
|
176
|
+
return ((buffer[offset] & 0xff) |
|
|
177
|
+
((buffer[offset + 1] & 0xff) << 8) |
|
|
178
|
+
((buffer[offset + 2] & 0xff) << 16) |
|
|
179
|
+
((buffer[offset + 3] & 0xff) << 24)) >>> 0 // >>> 0 to convert to unsigned
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @param {Uint8Array} buffer
|
|
184
|
+
* @param {number} value
|
|
185
|
+
* @param {number} offset
|
|
186
|
+
*/
|
|
187
|
+
export function writeUInt32LE(buffer, value, offset) {
|
|
188
|
+
if (offset < 0 || offset + 4 > buffer.length) throw new RangeError('Writing out of bounds')
|
|
189
|
+
buffer[offset] = value & 0xff
|
|
190
|
+
buffer[offset + 1] = (value >> 8) & 0xff
|
|
191
|
+
buffer[offset + 2] = (value >> 16) & 0xff
|
|
192
|
+
buffer[offset + 3] = (value >> 24) & 0xff
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {Uint8Array[]} buffers
|
|
197
|
+
*/
|
|
198
|
+
export function Uint8Array_concat(buffers) {
|
|
199
|
+
const totalLength = buffers.reduce((sum, buffer) => sum + buffer.length, 0)
|
|
200
|
+
const resultBuffer = new Uint8Array(totalLength)
|
|
201
|
+
let offset = 0
|
|
202
|
+
for (const buffer of buffers) {
|
|
203
|
+
resultBuffer.set(buffer, offset)
|
|
204
|
+
offset += buffer.length
|
|
205
|
+
}
|
|
206
|
+
return resultBuffer
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @param {*} array
|
|
211
|
+
* @param {'utf-8'|'hex'|'base64'} [encoding]
|
|
212
|
+
*/
|
|
213
|
+
export function Uint8Array_from(array, encoding) {
|
|
214
|
+
if (encoding == 'hex') {
|
|
215
|
+
array = new Uint8Array(array.match(/[\da-f]{2}/gi).map((h) => parseInt(h, 16)))
|
|
216
|
+
}
|
|
217
|
+
if (encoding == 'base64') {
|
|
218
|
+
array = Uint8Array.from(atob(array), (o) => o.codePointAt(0))
|
|
219
|
+
}
|
|
220
|
+
if (encoding == 'utf-8') {
|
|
221
|
+
array = new TextEncoder().encode(array)
|
|
222
|
+
}
|
|
223
|
+
if (typeof array === 'string') {
|
|
224
|
+
array = new TextEncoder().encode(array)
|
|
225
|
+
}
|
|
226
|
+
if (Array.isArray(array) || array instanceof Uint8Array) {
|
|
227
|
+
return new Uint8Array(array)
|
|
228
|
+
}
|
|
229
|
+
throw new TypeError('Argument must be an array or Uint8Array')
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @param {Uint8Array} buffer
|
|
234
|
+
* @param {'utf-8' | 'hex' | 'base64'} [encoding]
|
|
235
|
+
*/
|
|
236
|
+
export function Uint8Array_toString(buffer, encoding = 'utf-8') {
|
|
237
|
+
if (encoding == 'hex') {
|
|
238
|
+
return Array.from(buffer).map((b) => b.toString(16).padStart(2, "0")).join('')
|
|
239
|
+
}
|
|
240
|
+
if (encoding == 'base64') {
|
|
241
|
+
return btoa(String.fromCharCode(...buffer))
|
|
242
|
+
}
|
|
243
|
+
// utf-8
|
|
244
|
+
return new TextDecoder().decode(buffer)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @param {number} number
|
|
249
|
+
*/
|
|
250
|
+
function buildBufferNumberUInt32LE(number) {
|
|
251
|
+
let buffer = new Uint8Array(4)
|
|
252
|
+
writeUInt32LE(buffer, number, 0)
|
|
253
|
+
return buffer
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @param {string} string
|
|
258
|
+
*/
|
|
259
|
+
function buildBufferSizeString(string) {
|
|
260
|
+
let buffer = new TextEncoder().encode(string)
|
|
261
|
+
return Uint8Array_concat([
|
|
262
|
+
buildBufferNumberUInt32LE(buffer.length),
|
|
263
|
+
buffer,
|
|
264
|
+
])
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @param {Uint8Array} buffer
|
|
269
|
+
* @param {number} offset
|
|
270
|
+
*/
|
|
271
|
+
function readBufferSizeString(buffer, offset) {
|
|
272
|
+
let size = readUInt32LE(buffer, offset)
|
|
273
|
+
let start = offset + 4
|
|
274
|
+
let end = start + size
|
|
275
|
+
let string = new TextDecoder().decode(buffer.slice(start, end))
|
|
276
|
+
return { size: 4 + size, string }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function guid() {
|
|
280
|
+
let buffer = new Uint8Array(16)
|
|
281
|
+
if (globalThis.crypto) {
|
|
282
|
+
crypto.getRandomValues(buffer)
|
|
283
|
+
} else {
|
|
284
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
285
|
+
buffer[i] = Math.floor(Math.random() * 256)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return Array.from(buffer).map((o) => o.toString(16).padStart(2, '0')).join('')
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let tcpTunnelDataRecv = 0
|
|
292
|
+
let tcpTunnelDataSend = 0
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
*
|
|
296
|
+
* @param {TCP_TUNNEL_DATA} data
|
|
297
|
+
*/
|
|
298
|
+
export function printTcpTunnelData(data) {
|
|
299
|
+
return `id: ${`${data.srcId}:${data.dstId}`.padEnd(10)} channel: ${`${data.srcChannel}:${data.dstChannel}`.padEnd(10)} ${{
|
|
300
|
+
[TUNNEL_TCP_TYPE_INIT]: 'TUNNEL_TCP_TYPE_INIT ',
|
|
301
|
+
[TUNNEL_TCP_TYPE_LISTEN]: 'TUNNEL_TCP_TYPE_LISTEN ',
|
|
302
|
+
[TUNNEL_TCP_TYPE_ONLISTEN]: 'TUNNEL_TCP_TYPE_ONLISTEN ',
|
|
303
|
+
[TUNNEL_TCP_TYPE_CONNECT]: 'TUNNEL_TCP_TYPE_CONNECT ',
|
|
304
|
+
[TUNNEL_TCP_TYPE_ONCONNECT]: 'TUNNEL_TCP_TYPE_ONCONNECT ',
|
|
305
|
+
[TUNNEL_TCP_TYPE_DATA]: 'TUNNEL_TCP_TYPE_DATA ',
|
|
306
|
+
[TUNNEL_TCP_TYPE_ERROR]: 'TUNNEL_TCP_TYPE_ERROR ',
|
|
307
|
+
[TUNNEL_TCP_TYPE_CLOSE]: 'TUNNEL_TCP_TYPE_CLOSE ',
|
|
308
|
+
[TUNNEL_TCP_TYPE_PING]: 'TUNNEL_TCP_TYPE_PING ',
|
|
309
|
+
[TUNNEL_TCP_TYPE_PONG]: 'TUNNEL_TCP_TYPE_PONG ',
|
|
310
|
+
[TUNNEL_TCP_TYPE_ACK]: 'TUNNEL_TCP_TYPE_ACK ',
|
|
311
|
+
}[data.type]} recv: ${(tcpTunnelDataRecv)} send: ${(tcpTunnelDataSend)} size:${data.buffer.length}`
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
*
|
|
316
|
+
* @param {string} password
|
|
317
|
+
* @param {number} iterations
|
|
318
|
+
* @returns {Promise<[CryptoKey,Uint8Array]>}
|
|
319
|
+
*/
|
|
320
|
+
export async function buildKeyIv(password, iterations) {
|
|
321
|
+
if (!TUNNEL_TCP_WITH_CRYPTO) return [null, null]
|
|
322
|
+
if (!password) return [null, null]
|
|
323
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
324
|
+
"raw",
|
|
325
|
+
new TextEncoder().encode(password),
|
|
326
|
+
"PBKDF2",
|
|
327
|
+
false,
|
|
328
|
+
["deriveBits", "deriveKey"],
|
|
329
|
+
)
|
|
330
|
+
const salt = await crypto.subtle.digest("SHA-512", new TextEncoder().encode(password))
|
|
331
|
+
const pbkdf2Params = {
|
|
332
|
+
name: "PBKDF2",
|
|
333
|
+
salt,
|
|
334
|
+
iterations: iterations,
|
|
335
|
+
hash: "SHA-256",
|
|
336
|
+
}
|
|
337
|
+
const key = await crypto.subtle.deriveKey(
|
|
338
|
+
pbkdf2Params,
|
|
339
|
+
keyMaterial,
|
|
340
|
+
{ name: "AES-GCM", length: 256 },
|
|
341
|
+
true,
|
|
342
|
+
["encrypt", "decrypt"],
|
|
343
|
+
)
|
|
344
|
+
const iv = await crypto.subtle.deriveBits(
|
|
345
|
+
pbkdf2Params,
|
|
346
|
+
keyMaterial,
|
|
347
|
+
256,
|
|
348
|
+
)
|
|
349
|
+
return [key, new Uint8Array(iv)]
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
*
|
|
354
|
+
* @param {Uint8Array} data
|
|
355
|
+
* @param {CryptoKey} key
|
|
356
|
+
* @param {Uint8Array} iv
|
|
357
|
+
* @returns
|
|
358
|
+
*/
|
|
359
|
+
export async function encrypt(data, key, iv) {
|
|
360
|
+
if (!TUNNEL_TCP_WITH_CRYPTO) return data
|
|
361
|
+
if (!key) return data
|
|
362
|
+
const encryptedData = await crypto.subtle.encrypt(
|
|
363
|
+
{ name: 'AES-GCM', iv: iv }, key, data
|
|
364
|
+
)
|
|
365
|
+
return new Uint8Array(encryptedData)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* @param {Uint8Array} data
|
|
370
|
+
* @param {CryptoKey} key
|
|
371
|
+
* @param {Uint8Array} iv
|
|
372
|
+
* @returns
|
|
373
|
+
*/
|
|
374
|
+
export async function decrypt(data, key, iv) {
|
|
375
|
+
if (!TUNNEL_TCP_WITH_CRYPTO) return data
|
|
376
|
+
if (!key) return data
|
|
377
|
+
try {
|
|
378
|
+
const encryptedArray = data
|
|
379
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
380
|
+
{ name: 'AES-GCM', iv: iv }, key, encryptedArray
|
|
381
|
+
)
|
|
382
|
+
return new Uint8Array(decryptedData)
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error('decrypt error', error.message)
|
|
385
|
+
}
|
|
386
|
+
return new Uint8Array(0)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* @param {AbortSignal} signal
|
|
391
|
+
* @param {()=> Promise<void>} callback
|
|
392
|
+
*/
|
|
393
|
+
export async function timeWaitRetryLoop(signal, callback) {
|
|
394
|
+
let waitTime = 300
|
|
395
|
+
while (!signal.aborted) {
|
|
396
|
+
let time = performance.now()
|
|
397
|
+
try {
|
|
398
|
+
await callback()
|
|
399
|
+
} catch (error) {
|
|
400
|
+
console.error('timeWaitRetryLoop', error.message)
|
|
401
|
+
}
|
|
402
|
+
if (performance.now() - time > 10_000) {
|
|
403
|
+
waitTime = 300
|
|
404
|
+
}
|
|
405
|
+
await sleep(waitTime)
|
|
406
|
+
waitTime *= 2
|
|
407
|
+
if (waitTime > 60_000) {
|
|
408
|
+
waitTime = 60_000
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export const TUNNEL_TCP_TYPE_INIT = 0xa9b398d5 // 链接建立后返回id
|
|
414
|
+
export const TUNNEL_TCP_TYPE_LISTEN = 0xe41957d3
|
|
415
|
+
export const TUNNEL_TCP_TYPE_ONLISTEN = 0x20993e38
|
|
416
|
+
export const TUNNEL_TCP_TYPE_CONNECT = 0x11d949f8
|
|
417
|
+
export const TUNNEL_TCP_TYPE_ONCONNECT = 0x377b2181
|
|
418
|
+
export const TUNNEL_TCP_TYPE_DATA = 0x48678f39
|
|
419
|
+
export const TUNNEL_TCP_TYPE_ERROR = 0x8117f762
|
|
420
|
+
export const TUNNEL_TCP_TYPE_CLOSE = 0x72fd6470
|
|
421
|
+
export const TUNNEL_TCP_TYPE_PING = 0x4768e1ba
|
|
422
|
+
export const TUNNEL_TCP_TYPE_PONG = 0x106f43fb
|
|
423
|
+
export const TUNNEL_TCP_TYPE_ACK = 0xc5870539
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* @param {TCP_TUNNEL_DATA} box
|
|
427
|
+
*/
|
|
428
|
+
export function buildTcpTunnelData(box) {
|
|
429
|
+
let offset = 0
|
|
430
|
+
let header = new Uint8Array(20)
|
|
431
|
+
writeUInt32LE(header, box.type, offset); offset += 4
|
|
432
|
+
writeUInt32LE(header, box.srcId, offset); offset += 4
|
|
433
|
+
writeUInt32LE(header, box.dstId, offset); offset += 4
|
|
434
|
+
writeUInt32LE(header, box.srcChannel, offset); offset += 4
|
|
435
|
+
writeUInt32LE(header, box.dstChannel, offset); offset += 4
|
|
436
|
+
return Uint8Array_concat([header, box.buffer,])
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* @param {Uint8Array} buffer
|
|
441
|
+
*/
|
|
442
|
+
export function parseTcpTunnelData(buffer) {
|
|
443
|
+
let offset = 0
|
|
444
|
+
/** @type{*} */
|
|
445
|
+
let type = readUInt32LE(buffer, offset); offset += 4
|
|
446
|
+
let src_id = readUInt32LE(buffer, offset); offset += 4
|
|
447
|
+
let dst_id = readUInt32LE(buffer, offset); offset += 4
|
|
448
|
+
let src_channel = readUInt32LE(buffer, offset); offset += 4
|
|
449
|
+
let dst_channel = readUInt32LE(buffer, offset); offset += 4
|
|
450
|
+
let data = buffer.subarray(offset)
|
|
451
|
+
/** @type{TCP_TUNNEL_DATA} */
|
|
452
|
+
let box = { type, srcId: src_id, dstId: dst_id, srcChannel: src_channel, dstChannel: dst_channel, buffer: data }
|
|
453
|
+
return box
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @param {number} bufferTime
|
|
458
|
+
*/
|
|
459
|
+
export function createTimeBufferedTransformStream(bufferTime) {
|
|
460
|
+
if (!TUNNEL_TCP_TIME_BUFFERED) {
|
|
461
|
+
return new TransformStream()
|
|
462
|
+
}
|
|
463
|
+
let maxloop = Math.floor(Math.max(5_000 / bufferTime, 3))
|
|
464
|
+
/** @type{()=>void} */
|
|
465
|
+
let callback = null
|
|
466
|
+
const runCallback = () => {
|
|
467
|
+
if (callback != null) {
|
|
468
|
+
callback()
|
|
469
|
+
callback = null
|
|
470
|
+
}
|
|
471
|
+
if (loop-- < 0) {
|
|
472
|
+
clean()
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
let loop = 0
|
|
476
|
+
let queue = []
|
|
477
|
+
let time = performance.now()
|
|
478
|
+
let timer = null
|
|
479
|
+
let transform = new TransformStream({
|
|
480
|
+
async transform(chunk, controller) {
|
|
481
|
+
if (chunk.length < 100) {
|
|
482
|
+
loop += chunk.length
|
|
483
|
+
if (loop > maxloop) {
|
|
484
|
+
loop = maxloop
|
|
485
|
+
}
|
|
486
|
+
if (timer == null) {
|
|
487
|
+
timer = setInterval(runCallback, bufferTime)
|
|
488
|
+
console.info('create loop timer')
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (timer == null || chunk.length > 1024) {
|
|
492
|
+
runCallback()
|
|
493
|
+
controller.enqueue(chunk)
|
|
494
|
+
return
|
|
495
|
+
}
|
|
496
|
+
queue.push(chunk)
|
|
497
|
+
callback = () => {
|
|
498
|
+
if (queue.length > 0) {
|
|
499
|
+
controller.enqueue(Uint8Array_concat(queue))
|
|
500
|
+
queue = []
|
|
501
|
+
time = performance.now()
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (performance.now() - time > bufferTime) {
|
|
505
|
+
runCallback()
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
flush() {
|
|
509
|
+
clean()
|
|
510
|
+
}
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
const clean = () => {
|
|
514
|
+
if (timer == null) {
|
|
515
|
+
return
|
|
516
|
+
}
|
|
517
|
+
console.info('clean loop timer')
|
|
518
|
+
clearInterval(timer)
|
|
519
|
+
timer = null
|
|
520
|
+
runCallback()
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return transform
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* @param {Map<number,SocketChannel>} channelMap
|
|
528
|
+
* @param {number} channelId
|
|
529
|
+
* @param {WritableStreamDefaultWriter<Uint8Array>} encodeWriter
|
|
530
|
+
*/
|
|
531
|
+
export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
|
|
532
|
+
let channel = channelMap.get(channelId)
|
|
533
|
+
let socket = channel.socket
|
|
534
|
+
let signal = Promise_withResolvers()
|
|
535
|
+
signal.resolve()
|
|
536
|
+
let sendPackSize = 0
|
|
537
|
+
let recvPackSize = 0
|
|
538
|
+
channel.notify = (size) => {
|
|
539
|
+
recvPackSize = size
|
|
540
|
+
signal.resolve()
|
|
541
|
+
}
|
|
542
|
+
let [clientKey, clientIv] = channel.key_iv
|
|
543
|
+
let bufferedTransform = createTimeBufferedTransformStream(50)
|
|
544
|
+
Readable.toWeb(socket).pipeThrough(bufferedTransform).pipeTo(new WritableStream({
|
|
545
|
+
async write(chunk) {
|
|
546
|
+
const buffer = await encrypt(chunk, clientKey, clientIv)
|
|
547
|
+
let bufferPackSize = sendPackSize - recvPackSize
|
|
548
|
+
if (bufferPackSize > 10) {
|
|
549
|
+
signal.resolve()
|
|
550
|
+
signal = Promise_withResolvers()
|
|
551
|
+
if (DEBUG_TUNNEL_TCP) {
|
|
552
|
+
console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:', recvPackSize, ' bufferPackSize:', bufferPackSize)
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
await signal.promise
|
|
556
|
+
await encodeWriter.write(buildTcpTunnelData({
|
|
557
|
+
type: TUNNEL_TCP_TYPE_DATA,
|
|
558
|
+
srcId: channel.srcId,
|
|
559
|
+
srcChannel: channel.srcChannel,
|
|
560
|
+
dstId: channel.dstId,
|
|
561
|
+
dstChannel: channel.dstChannel,
|
|
562
|
+
buffer: buffer,
|
|
563
|
+
})).catch((err) => { console.error('web stream write error', err.message) })
|
|
564
|
+
sendPackSize++
|
|
565
|
+
},
|
|
566
|
+
async close() {
|
|
567
|
+
encodeWriter.write(buildTcpTunnelData({
|
|
568
|
+
type: TUNNEL_TCP_TYPE_CLOSE,
|
|
569
|
+
srcId: channel.srcId,
|
|
570
|
+
srcChannel: channel.srcChannel,
|
|
571
|
+
dstId: channel.dstId,
|
|
572
|
+
dstChannel: channel.dstChannel,
|
|
573
|
+
buffer: new Uint8Array(0),
|
|
574
|
+
})).catch((err) => { console.error('web stream write error', err.message) })
|
|
575
|
+
channelMap.delete(channelId)
|
|
576
|
+
}
|
|
577
|
+
})).catch((err) => {
|
|
578
|
+
console.error('web stream error', err.message)
|
|
579
|
+
})
|
|
580
|
+
socket.on('error', (err) => {
|
|
581
|
+
console.error('pipeSocketDataWithChannel on error ', err.message)
|
|
582
|
+
encodeWriter.write(buildTcpTunnelData({
|
|
583
|
+
type: TUNNEL_TCP_TYPE_ERROR,
|
|
584
|
+
srcId: channel.srcId,
|
|
585
|
+
srcChannel: channel.srcChannel,
|
|
586
|
+
dstId: channel.dstId,
|
|
587
|
+
dstChannel: channel.dstChannel,
|
|
588
|
+
buffer: new Uint8Array(0),
|
|
589
|
+
})).catch((err) => { console.error('web stream write error', err.message) })
|
|
590
|
+
channelMap.delete(channelId)
|
|
591
|
+
})
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* @param {TunnelTcpServerHelperParam} param
|
|
596
|
+
* @param {TCP_TUNNEL_DATA} data
|
|
597
|
+
*/
|
|
598
|
+
function natTunnelData(param, data) {
|
|
599
|
+
// if (data.dstId != param.serverId) {
|
|
600
|
+
// return
|
|
601
|
+
// }
|
|
602
|
+
// /** @type{Map<number,number>} */
|
|
603
|
+
// let natId = new Map()
|
|
604
|
+
// /** @type{Map<number,number>} */
|
|
605
|
+
// let natChannel = new Map()
|
|
606
|
+
// data.dstId = natId.get(data.dstId)
|
|
607
|
+
// data.dstChannel = natChannel.get(data.dstChannel)
|
|
608
|
+
// data.srcId = natId.get(data.srcId)
|
|
609
|
+
// data.srcChannel = natChannel.get(data.srcChannel)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* @param {TunnelTcpServerHelperParam} param
|
|
614
|
+
* @param {WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>} encodeWriter
|
|
615
|
+
* @param {Uint8Array<ArrayBufferLike>} chunk
|
|
616
|
+
*/
|
|
617
|
+
async function dispatchServerBufferData(param, encodeWriter, chunk) {
|
|
618
|
+
let data = parseTcpTunnelData(chunk)
|
|
619
|
+
if (DEBUG_TUNNEL_TCP) {
|
|
620
|
+
tcpTunnelDataRecv += chunk.length
|
|
621
|
+
console.info('recv', printTcpTunnelData(data))
|
|
622
|
+
}
|
|
623
|
+
natTunnelData(param, data)
|
|
624
|
+
if (data.type == TUNNEL_TCP_TYPE_LISTEN) {
|
|
625
|
+
/** @type{TUNNEL_TCP_DATA_LISTEN} */
|
|
626
|
+
let o = JSON.parse(Uint8Array_toString(data.buffer))
|
|
627
|
+
param.listenMap.set(o.key, data.srcId)
|
|
628
|
+
data.type = TUNNEL_TCP_TYPE_ONLISTEN
|
|
629
|
+
data.dstId = data.srcId
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (data.type == TUNNEL_TCP_TYPE_CONNECT) {
|
|
633
|
+
/** @type{TUNNEL_TCP_DATA_CONNECT} */
|
|
634
|
+
let o = JSON.parse(Uint8Array_toString(data.buffer))
|
|
635
|
+
let dstId = param.listenMap.get(o.key)
|
|
636
|
+
data.dstId = dstId
|
|
637
|
+
if (!param.dstMap.has(dstId)) {
|
|
638
|
+
param.listenMap.delete(o.key)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
let dstWriter = param.dstMap.get(data.dstId)
|
|
643
|
+
if (!dstWriter) {
|
|
644
|
+
data.type = TUNNEL_TCP_TYPE_CLOSE
|
|
645
|
+
data.dstId = data.srcId
|
|
646
|
+
data.dstChannel = data.srcChannel
|
|
647
|
+
let srcWriter = param.dstMap.get(data.dstId)
|
|
648
|
+
if (srcWriter) {
|
|
649
|
+
await srcWriter.encodeWriter.write(buildTcpTunnelData(data))
|
|
650
|
+
} else {
|
|
651
|
+
await encodeWriter.write(buildTcpTunnelData(data))
|
|
652
|
+
}
|
|
653
|
+
} else {
|
|
654
|
+
await dstWriter.encodeWriter.write(buildTcpTunnelData(data))
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* @param {TunnelTcpClientHelperParam} param
|
|
660
|
+
* @param {{ (): Promise<void>; }} setup
|
|
661
|
+
* @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} listenKeyParamMap
|
|
662
|
+
* @param {Map<number, SocketChannel>} channelMap
|
|
663
|
+
* @param {WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>} encodeWriter
|
|
664
|
+
* @param {Uint8Array<ArrayBufferLike>} buffer
|
|
665
|
+
*/
|
|
666
|
+
async function dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer) {
|
|
667
|
+
let data = parseTcpTunnelData(buffer)
|
|
668
|
+
if (DEBUG_TUNNEL_TCP) {
|
|
669
|
+
tcpTunnelDataRecv += buffer.length
|
|
670
|
+
console.info('recv', printTcpTunnelData(data))
|
|
671
|
+
}
|
|
672
|
+
if (data.type == TUNNEL_TCP_TYPE_INIT) {
|
|
673
|
+
param.clientDataId = data.dstId
|
|
674
|
+
await setup()
|
|
675
|
+
}
|
|
676
|
+
if (data.type == TUNNEL_TCP_TYPE_ONLISTEN) {
|
|
677
|
+
}
|
|
678
|
+
if (data.type == TUNNEL_TCP_TYPE_CONNECT) {
|
|
679
|
+
/** @type{TUNNEL_TCP_DATA_CONNECT} */
|
|
680
|
+
let o = JSON.parse(Uint8Array_toString(data.buffer))
|
|
681
|
+
let { host, port, key_iv } = listenKeyParamMap.get(o.key)
|
|
682
|
+
let connectSocket = net.createConnection({ host: host || '127.0.0.1', port: port })
|
|
683
|
+
let channelId = param.uniqueId++
|
|
684
|
+
/** @type{SocketChannel} */
|
|
685
|
+
let channel = {
|
|
686
|
+
writer: Writable.toWeb(connectSocket).getWriter(),
|
|
687
|
+
socket: connectSocket,
|
|
688
|
+
srcId: data.dstId,
|
|
689
|
+
dstId: data.srcId,
|
|
690
|
+
srcChannel: channelId,
|
|
691
|
+
dstChannel: data.srcChannel,
|
|
692
|
+
recvPackSize: 0,
|
|
693
|
+
key_iv,
|
|
694
|
+
notify: null,
|
|
695
|
+
}
|
|
696
|
+
channelMap.set(channelId, channel)
|
|
697
|
+
connectSocket.on('connect', () => {
|
|
698
|
+
encodeWriter.write(buildTcpTunnelData({
|
|
699
|
+
type: TUNNEL_TCP_TYPE_ONCONNECT,
|
|
700
|
+
srcId: channel.srcId,
|
|
701
|
+
srcChannel: channel.srcChannel,
|
|
702
|
+
dstId: channel.dstId,
|
|
703
|
+
dstChannel: channel.dstChannel,
|
|
704
|
+
buffer: data.buffer
|
|
705
|
+
}))
|
|
706
|
+
})
|
|
707
|
+
pipeSocketDataWithChannel(channelMap, channelId, encodeWriter)
|
|
708
|
+
}
|
|
709
|
+
if (data.type == TUNNEL_TCP_TYPE_ONCONNECT) {
|
|
710
|
+
let channelId = data.dstChannel
|
|
711
|
+
let channel = channelMap.get(channelId)
|
|
712
|
+
if (channel) {
|
|
713
|
+
channel.dstId = data.srcId
|
|
714
|
+
channel.dstChannel = data.srcChannel
|
|
715
|
+
pipeSocketDataWithChannel(channelMap, channelId, encodeWriter)
|
|
716
|
+
/** @type{TUNNEL_TCP_DATA_PINGPONG} */
|
|
717
|
+
let pingData = { time: Date.now() }
|
|
718
|
+
await encodeWriter.write(buildTcpTunnelData({
|
|
719
|
+
type: TUNNEL_TCP_TYPE_PING,
|
|
720
|
+
srcId: channel.srcId,
|
|
721
|
+
srcChannel: channel.srcChannel,
|
|
722
|
+
dstId: channel.dstId,
|
|
723
|
+
dstChannel: channel.dstChannel,
|
|
724
|
+
buffer: Uint8Array_from(JSON.stringify(pingData)),
|
|
725
|
+
}))
|
|
726
|
+
} else {
|
|
727
|
+
await closeRemoteChannel(encodeWriter, data)
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (data.type == TUNNEL_TCP_TYPE_PING) {
|
|
731
|
+
let channelId = data.dstChannel
|
|
732
|
+
let channel = channelMap.get(channelId)
|
|
733
|
+
if (channel) {
|
|
734
|
+
channel.srcId = data.dstId
|
|
735
|
+
channel.dstId = data.srcId
|
|
736
|
+
await encodeWriter.write(buildTcpTunnelData({
|
|
737
|
+
type: TUNNEL_TCP_TYPE_PONG,
|
|
738
|
+
srcId: channel.srcId,
|
|
739
|
+
srcChannel: channel.srcChannel,
|
|
740
|
+
dstId: channel.dstId,
|
|
741
|
+
dstChannel: channel.dstChannel,
|
|
742
|
+
buffer: data.buffer,
|
|
743
|
+
}))
|
|
744
|
+
} else {
|
|
745
|
+
await closeRemoteChannel(encodeWriter, data)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (data.type == TUNNEL_TCP_TYPE_PONG) {
|
|
749
|
+
let channelId = data.dstChannel
|
|
750
|
+
let channel = channelMap.get(channelId)
|
|
751
|
+
if (channel) {
|
|
752
|
+
channel.srcId = data.dstId
|
|
753
|
+
channel.dstId = data.srcId
|
|
754
|
+
/** @type{TUNNEL_TCP_DATA_PINGPONG} */
|
|
755
|
+
let pingData = JSON.parse(Uint8Array_toString(data.buffer))
|
|
756
|
+
console.info('createTunnelTcpClientHelper ', 'ping time', (Date.now() - pingData.time))
|
|
757
|
+
} else {
|
|
758
|
+
await closeRemoteChannel(encodeWriter, data)
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
if (data.type == TUNNEL_TCP_TYPE_CLOSE) {
|
|
762
|
+
let channelId = data.dstChannel
|
|
763
|
+
let channel = channelMap.get(channelId)
|
|
764
|
+
if (channel) {
|
|
765
|
+
channelMap.delete(channelId)
|
|
766
|
+
channel.socket.destroy()
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (data.type == TUNNEL_TCP_TYPE_ERROR) {
|
|
770
|
+
let channelId = data.dstChannel
|
|
771
|
+
let channel = channelMap.get(channelId)
|
|
772
|
+
if (channel) {
|
|
773
|
+
channelMap.delete(channelId)
|
|
774
|
+
channel.socket.destroy()
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (data.type == TUNNEL_TCP_TYPE_DATA) {
|
|
778
|
+
let channelId = data.dstChannel
|
|
779
|
+
let channel = channelMap.get(channelId)
|
|
780
|
+
if (channel) {
|
|
781
|
+
channel.srcId = data.dstId
|
|
782
|
+
channel.dstId = data.srcId
|
|
783
|
+
let [clientKey, clientIv] = channel.key_iv
|
|
784
|
+
let buffer = await decrypt(data.buffer, clientKey, clientIv)
|
|
785
|
+
if (buffer.length > 0) {
|
|
786
|
+
await channel.writer.write(buffer)
|
|
787
|
+
}
|
|
788
|
+
channel.recvPackSize++
|
|
789
|
+
await sendAck(encodeWriter, channel)
|
|
790
|
+
} else {
|
|
791
|
+
await closeRemoteChannel(encodeWriter, data)
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (data.type == TUNNEL_TCP_TYPE_ACK) {
|
|
795
|
+
let channelId = data.dstChannel
|
|
796
|
+
let channel = channelMap.get(channelId)
|
|
797
|
+
if (channel) {
|
|
798
|
+
channel.srcId = data.dstId
|
|
799
|
+
channel.dstId = data.srcId
|
|
800
|
+
let size = readUInt32LE(data.buffer, 0)
|
|
801
|
+
channel.notify(size)
|
|
802
|
+
} else {
|
|
803
|
+
await closeRemoteChannel(encodeWriter, data)
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* @param {WritableStreamDefaultWriter<Uint8Array>} encodeWriter
|
|
810
|
+
* @param {SocketChannel} channel
|
|
811
|
+
*/
|
|
812
|
+
export async function sendAck(encodeWriter, channel) {
|
|
813
|
+
if (channel.recvPackSize % 5 != 0) {
|
|
814
|
+
return
|
|
815
|
+
}
|
|
816
|
+
let sizeBuffer = new Uint8Array(4)
|
|
817
|
+
writeUInt32LE(sizeBuffer, channel.recvPackSize, 0)
|
|
818
|
+
await encodeWriter.write(buildTcpTunnelData({
|
|
819
|
+
type: TUNNEL_TCP_TYPE_ACK,
|
|
820
|
+
srcId: channel.srcId,
|
|
821
|
+
srcChannel: channel.srcChannel,
|
|
822
|
+
dstId: channel.dstId,
|
|
823
|
+
dstChannel: channel.dstChannel,
|
|
824
|
+
buffer: sizeBuffer,
|
|
825
|
+
}))
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* @param {WritableStreamDefaultWriter<Uint8Array>} encodeWriter
|
|
830
|
+
* @param {TCP_TUNNEL_DATA} data
|
|
831
|
+
*/
|
|
832
|
+
export async function closeRemoteChannel(encodeWriter, data) {
|
|
833
|
+
await encodeWriter.write(buildTcpTunnelData({
|
|
834
|
+
type: TUNNEL_TCP_TYPE_CLOSE,
|
|
835
|
+
srcId: data.dstId,
|
|
836
|
+
srcChannel: data.dstChannel,
|
|
837
|
+
dstId: data.srcId,
|
|
838
|
+
dstChannel: data.srcChannel,
|
|
839
|
+
buffer: new Uint8Array(0),
|
|
840
|
+
})).catch((err) => { console.error('closeRemoteChannel stream write error', err.message) })
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* @param {TunnelTcpServerHelperParam} param
|
|
845
|
+
*/
|
|
846
|
+
export function createTunnelTcpServerHelper(param) {
|
|
847
|
+
let server_key_iv = buildKeyIv(param.serverKey, 10)
|
|
848
|
+
let encode = createEncodeStream(server_key_iv)
|
|
849
|
+
let decode = createDecodeStream(server_key_iv)
|
|
850
|
+
let encodeWriter = encode.writable.getWriter()
|
|
851
|
+
|
|
852
|
+
if (DEBUG_TUNNEL_TCP) {
|
|
853
|
+
let writer = encodeWriter
|
|
854
|
+
encodeWriter = new WritableStream({
|
|
855
|
+
async write(chunk) {
|
|
856
|
+
tcpTunnelDataSend += chunk.length
|
|
857
|
+
let data = parseTcpTunnelData(chunk)
|
|
858
|
+
console.info('send', printTcpTunnelData(data))
|
|
859
|
+
writer.write(chunk)
|
|
860
|
+
}
|
|
861
|
+
}).getWriter()
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
decode.readable.pipeTo(new WritableStream({
|
|
865
|
+
async write(chunk) {
|
|
866
|
+
try {
|
|
867
|
+
await dispatchServerBufferData(param, encodeWriter, chunk)
|
|
868
|
+
} catch (error) {
|
|
869
|
+
console.error('decode.readable.pipeTo.write', error.message)
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}))
|
|
873
|
+
|
|
874
|
+
let id = param.uniqueId++
|
|
875
|
+
param.dstMap.set(id, { id, encodeWriter: encodeWriter })
|
|
876
|
+
encodeWriter.write(buildTcpTunnelData({
|
|
877
|
+
type: TUNNEL_TCP_TYPE_INIT,
|
|
878
|
+
srcId: 0,
|
|
879
|
+
srcChannel: 0,
|
|
880
|
+
dstId: id,
|
|
881
|
+
dstChannel: 0,
|
|
882
|
+
buffer: new Uint8Array(0),
|
|
883
|
+
}))
|
|
884
|
+
|
|
885
|
+
/** @type{TUNNEL_TCP_SERVER_HELPER} */
|
|
886
|
+
let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, dstId: id, }
|
|
887
|
+
return helper
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* @param {TunnelTcpClientHelperParam} param
|
|
892
|
+
*/
|
|
893
|
+
export function createTunnelTcpClientHelper(param) {
|
|
894
|
+
/** @type{Map<number,SocketChannel>} */
|
|
895
|
+
let channelMap = new Map()
|
|
896
|
+
|
|
897
|
+
/** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} */
|
|
898
|
+
let listenKeyParamMap = new Map()
|
|
899
|
+
|
|
900
|
+
let server_key_iv = buildKeyIv(param.serverKey, 10)
|
|
901
|
+
|
|
902
|
+
param.signal.addEventListener('abort', () => {
|
|
903
|
+
channelMap.values().forEach(o => {
|
|
904
|
+
o.socket.destroy()
|
|
905
|
+
})
|
|
906
|
+
})
|
|
907
|
+
|
|
908
|
+
let encode = createEncodeStream(server_key_iv)
|
|
909
|
+
let decode = createDecodeStream(server_key_iv)
|
|
910
|
+
let encodeWriter = encode.writable.getWriter()
|
|
911
|
+
if (DEBUG_TUNNEL_TCP) {
|
|
912
|
+
let writer = encodeWriter
|
|
913
|
+
encodeWriter = new WritableStream({
|
|
914
|
+
async write(chunk) {
|
|
915
|
+
tcpTunnelDataSend += chunk.length
|
|
916
|
+
let data = parseTcpTunnelData(chunk)
|
|
917
|
+
console.info('send', printTcpTunnelData(data))
|
|
918
|
+
writer.write(chunk)
|
|
919
|
+
}
|
|
920
|
+
}).getWriter()
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
decode.readable.pipeTo(new WritableStream({
|
|
924
|
+
async write(buffer) {
|
|
925
|
+
try {
|
|
926
|
+
await dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer)
|
|
927
|
+
} catch (error) {
|
|
928
|
+
console.error('decode.readable.pipeTo.write', error.message)
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}))
|
|
932
|
+
|
|
933
|
+
let outParam = param
|
|
934
|
+
let listenParams = new Set()
|
|
935
|
+
|
|
936
|
+
async function setup() {
|
|
937
|
+
channelMap.forEach((channel) => {
|
|
938
|
+
channel.srcId = param.clientDataId
|
|
939
|
+
/** @type{TUNNEL_TCP_DATA_PINGPONG} */
|
|
940
|
+
let pingData = { time: Date.now() }
|
|
941
|
+
encodeWriter.write(buildTcpTunnelData({
|
|
942
|
+
type: TUNNEL_TCP_TYPE_PING,
|
|
943
|
+
srcId: channel.srcId,
|
|
944
|
+
srcChannel: channel.srcChannel,
|
|
945
|
+
dstId: channel.dstId,
|
|
946
|
+
dstChannel: channel.dstChannel,
|
|
947
|
+
buffer: Uint8Array_from(JSON.stringify(pingData)),
|
|
948
|
+
}))
|
|
949
|
+
})
|
|
950
|
+
for (const param of listenParams) {
|
|
951
|
+
await listen(param)
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* @param {{
|
|
957
|
+
* clientKey?:string;
|
|
958
|
+
* tunnelKey:string;
|
|
959
|
+
* host?:string;
|
|
960
|
+
* port:number;
|
|
961
|
+
* }} param
|
|
962
|
+
*/
|
|
963
|
+
async function listen(param) {
|
|
964
|
+
listenParams.add(param)
|
|
965
|
+
console.info('listenParams size', listenParams.size)
|
|
966
|
+
if (outParam.clientDataId < 1) {
|
|
967
|
+
console.info('skip send listen dataId == 0')
|
|
968
|
+
return
|
|
969
|
+
}
|
|
970
|
+
let key = sha512(param.tunnelKey)
|
|
971
|
+
let key_iv = await buildKeyIv(param.clientKey, 10)
|
|
972
|
+
listenKeyParamMap.set(key, { host: param.host, port: param.port, key_iv })
|
|
973
|
+
/** @type{TUNNEL_TCP_DATA_LISTEN} */
|
|
974
|
+
let listenData = { key: key }
|
|
975
|
+
await encodeWriter.write(buildTcpTunnelData({
|
|
976
|
+
type: TUNNEL_TCP_TYPE_LISTEN,
|
|
977
|
+
srcId: outParam.clientDataId,
|
|
978
|
+
srcChannel: 0,
|
|
979
|
+
dstId: 0,
|
|
980
|
+
dstChannel: 0,
|
|
981
|
+
buffer: Uint8Array_from(JSON.stringify(listenData)),
|
|
982
|
+
}))
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* @param {{
|
|
987
|
+
* clientKey?:string;
|
|
988
|
+
* tunnelKey: string;
|
|
989
|
+
* port: number;
|
|
990
|
+
* }} param
|
|
991
|
+
*/
|
|
992
|
+
async function connect(param) {
|
|
993
|
+
let key_iv = await buildKeyIv(param.clientKey, 10)
|
|
994
|
+
let server = net.createServer((socket) => {
|
|
995
|
+
let channelId = outParam.uniqueId++
|
|
996
|
+
socket.on('error', (err) => {
|
|
997
|
+
console.error('createTunnelTcpClientHelper on socket error', err.message)
|
|
998
|
+
channelMap.delete(channelId)
|
|
999
|
+
})
|
|
1000
|
+
/** @type{TUNNEL_TCP_DATA_CONNECT} */
|
|
1001
|
+
let connectData = { key: sha512(param.tunnelKey) }
|
|
1002
|
+
/** @type{SocketChannel} */
|
|
1003
|
+
let channel = {
|
|
1004
|
+
writer: Writable.toWeb(socket).getWriter(),
|
|
1005
|
+
socket,
|
|
1006
|
+
srcId: outParam.clientDataId,
|
|
1007
|
+
srcChannel: channelId,
|
|
1008
|
+
dstId: 0,
|
|
1009
|
+
dstChannel: 0,
|
|
1010
|
+
recvPackSize: 0,
|
|
1011
|
+
key_iv,
|
|
1012
|
+
notify: null,
|
|
1013
|
+
}
|
|
1014
|
+
channelMap.set(channelId, channel)
|
|
1015
|
+
encodeWriter.write(buildTcpTunnelData({
|
|
1016
|
+
type: TUNNEL_TCP_TYPE_CONNECT,
|
|
1017
|
+
srcId: channel.srcId,
|
|
1018
|
+
srcChannel: channel.srcChannel,
|
|
1019
|
+
dstId: channel.dstId,
|
|
1020
|
+
dstChannel: channel.dstChannel,
|
|
1021
|
+
buffer: Uint8Array_from(JSON.stringify(connectData)),
|
|
1022
|
+
}))
|
|
1023
|
+
}).listen(param.port)
|
|
1024
|
+
server.on('error', (err) => {
|
|
1025
|
+
console.error('createTunnelTcpClientHelper connect on server error', err.message)
|
|
1026
|
+
})
|
|
1027
|
+
outParam.signal.addEventListener('abort', () => { server.close() })
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
/** @type{TUNNEL_TCP_CLIENT_HELPER} */
|
|
1031
|
+
let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, listen, connect, param: outParam }
|
|
1032
|
+
return helper
|
|
1033
|
+
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* @param {{
|
|
1039
|
+
* signal:AbortSignal;
|
|
1040
|
+
* serverKey:string;
|
|
1041
|
+
* port:number
|
|
1042
|
+
* }} param
|
|
1043
|
+
*/
|
|
1044
|
+
export function createTunnelTcpServerSocket(param) {
|
|
1045
|
+
/** @type{TunnelTcpServerHelperParam} */
|
|
1046
|
+
let helperParam = {
|
|
1047
|
+
serverKey: param.serverKey,
|
|
1048
|
+
uniqueId: 1,
|
|
1049
|
+
listenMap: new Map(),
|
|
1050
|
+
dstMap: new Map(),
|
|
1051
|
+
}
|
|
1052
|
+
let server = net.createServer(async (socket) => {
|
|
1053
|
+
let helper = createTunnelTcpServerHelper(helperParam)
|
|
1054
|
+
helper.readable.pipeTo(Writable.toWeb(socket)).catch((err) => {
|
|
1055
|
+
console.error('web stream error', err.message)
|
|
1056
|
+
})
|
|
1057
|
+
Readable.toWeb(socket).pipeTo(helper.writable).catch((err) => {
|
|
1058
|
+
console.error('web stream error', err.message)
|
|
1059
|
+
})
|
|
1060
|
+
socket.on('end', () => {
|
|
1061
|
+
console.info('createTunnelTcpServerSocket socket on end')
|
|
1062
|
+
helperParam.dstMap.delete(helper.dstId)
|
|
1063
|
+
})
|
|
1064
|
+
socket.on('close', () => {
|
|
1065
|
+
console.info('createTunnelTcpServerSocket socket on close')
|
|
1066
|
+
helperParam.dstMap.delete(helper.dstId)
|
|
1067
|
+
})
|
|
1068
|
+
socket.on('error', (err) => {
|
|
1069
|
+
console.error('createTunnelTcpServerSocket socket on error', err.message)
|
|
1070
|
+
helperParam.dstMap.delete(helper.dstId)
|
|
1071
|
+
})
|
|
1072
|
+
}).listen(param.port)
|
|
1073
|
+
param.signal.addEventListener('abort', () => {
|
|
1074
|
+
server.close()
|
|
1075
|
+
})
|
|
1076
|
+
return server
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* @param {{
|
|
1081
|
+
* signal:AbortSignal;
|
|
1082
|
+
* serverKey?:string;
|
|
1083
|
+
* serverHost:string;
|
|
1084
|
+
* serverPort:number;
|
|
1085
|
+
* }} param
|
|
1086
|
+
*/
|
|
1087
|
+
export function createTunnelTcpClientSocket(param) {
|
|
1088
|
+
let helper = createTunnelTcpClientHelper({
|
|
1089
|
+
serverKey: param.serverKey,
|
|
1090
|
+
uniqueId: 1,
|
|
1091
|
+
clientDataId: 0,
|
|
1092
|
+
signal: param.signal,
|
|
1093
|
+
})
|
|
1094
|
+
helper.writer = helper.writable.getWriter()
|
|
1095
|
+
let signal = Promise_withResolvers()
|
|
1096
|
+
/** @type{WritableStreamDefaultWriter<Uint8Array>} */
|
|
1097
|
+
let socketWriter = null
|
|
1098
|
+
helper.readable.pipeTo(new WritableStream({
|
|
1099
|
+
async write(chunk) {
|
|
1100
|
+
while (!param.signal.aborted && socketWriter == null) {
|
|
1101
|
+
await signal.promise
|
|
1102
|
+
}
|
|
1103
|
+
if (!param.signal.aborted) {
|
|
1104
|
+
await socketWriter.write(chunk)
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}))
|
|
1108
|
+
async function connectSocket() {
|
|
1109
|
+
let promise = Promise_withResolvers()
|
|
1110
|
+
let socket = net.createConnection({
|
|
1111
|
+
host: param.serverHost,
|
|
1112
|
+
port: param.serverPort,
|
|
1113
|
+
})
|
|
1114
|
+
socket.once('connect', () => {
|
|
1115
|
+
socketWriter = Writable.toWeb(socket).getWriter()
|
|
1116
|
+
Readable.toWeb(socket).pipeTo(new WritableStream({
|
|
1117
|
+
async write(chunk) {
|
|
1118
|
+
await helper.writer.write(chunk)
|
|
1119
|
+
}
|
|
1120
|
+
})).catch((err) => { console.error('web stream error', err.message) })
|
|
1121
|
+
signal.resolve()
|
|
1122
|
+
})
|
|
1123
|
+
socket.on('error', (err) => {
|
|
1124
|
+
console.error('createTunnelTcpClientSocket on error', err.message)
|
|
1125
|
+
promise.resolve()
|
|
1126
|
+
})
|
|
1127
|
+
socket.on('close', (err) => {
|
|
1128
|
+
console.info('createTunnelTcpClientSocket on close')
|
|
1129
|
+
promise.resolve()
|
|
1130
|
+
})
|
|
1131
|
+
const listenerAC = () => { socket.destroy() }
|
|
1132
|
+
param.signal.addEventListener('abort', listenerAC)
|
|
1133
|
+
await promise.promise
|
|
1134
|
+
param.signal.removeEventListener('abort', listenerAC)
|
|
1135
|
+
socketWriter = null
|
|
1136
|
+
signal.resolve()
|
|
1137
|
+
signal = Promise_withResolvers()
|
|
1138
|
+
}
|
|
1139
|
+
timeWaitRetryLoop(param.signal, async () => {
|
|
1140
|
+
console.info('createTunnelTcpClientSocket timeWaitRetryLoop', 'connectSocket')
|
|
1141
|
+
await connectSocket()
|
|
1142
|
+
})
|
|
1143
|
+
|
|
1144
|
+
return helper
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* @param {{
|
|
1150
|
+
* path:string;
|
|
1151
|
+
* wss:WebSocketServer;
|
|
1152
|
+
* signal:AbortSignal;
|
|
1153
|
+
* serverKey:string;
|
|
1154
|
+
* }} param
|
|
1155
|
+
*/
|
|
1156
|
+
export function createTunnelTcpServerWebSocket(param) {
|
|
1157
|
+
/** @type{TunnelTcpServerHelperParam} */
|
|
1158
|
+
let helperParam = {
|
|
1159
|
+
serverKey: param.serverKey,
|
|
1160
|
+
uniqueId: 1,
|
|
1161
|
+
listenMap: new Map(),
|
|
1162
|
+
dstMap: new Map(),
|
|
1163
|
+
}
|
|
1164
|
+
const wss = param.wss
|
|
1165
|
+
wss.on('connection', (ws, req) => {
|
|
1166
|
+
if (req.url !== param.path) {
|
|
1167
|
+
console.error('valid path error', req.url)
|
|
1168
|
+
ws.close()
|
|
1169
|
+
return
|
|
1170
|
+
}
|
|
1171
|
+
let helper = createTunnelTcpServerHelper(helperParam)
|
|
1172
|
+
helper.writer = helper.writable.getWriter()
|
|
1173
|
+
helper.readable.pipeTo(new WritableStream({
|
|
1174
|
+
async write(chunk) {
|
|
1175
|
+
await new Promise((resolve) => {
|
|
1176
|
+
ws.send(chunk, resolve)
|
|
1177
|
+
})
|
|
1178
|
+
}
|
|
1179
|
+
}))
|
|
1180
|
+
|
|
1181
|
+
ws.on('message', async (/**@type{*}*/buffer) => {
|
|
1182
|
+
if (helper.writer.desiredSize <= 0) {
|
|
1183
|
+
ws.pause()
|
|
1184
|
+
}
|
|
1185
|
+
await helper.writer.write(buffer)
|
|
1186
|
+
ws.resume()
|
|
1187
|
+
})
|
|
1188
|
+
ws.on('end', () => {
|
|
1189
|
+
console.info('createTunnelTcpServerWebSocket connection ws on end')
|
|
1190
|
+
helperParam.dstMap.delete(helper.dstId)
|
|
1191
|
+
})
|
|
1192
|
+
ws.on('close', (code) => {
|
|
1193
|
+
console.info('createTunnelTcpServerWebSocket connection ws on close', code)
|
|
1194
|
+
helperParam.dstMap.delete(helper.dstId)
|
|
1195
|
+
})
|
|
1196
|
+
ws.on('error', (err) => {
|
|
1197
|
+
console.error('createTunnelTcpServerWebSocket connection ws on error', err.message)
|
|
1198
|
+
helperParam.dstMap.delete(helper.dstId)
|
|
1199
|
+
})
|
|
1200
|
+
})
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* @param {{
|
|
1205
|
+
* signal:AbortSignal;
|
|
1206
|
+
* serverKey:string;
|
|
1207
|
+
* url:string;
|
|
1208
|
+
* }} param
|
|
1209
|
+
*/
|
|
1210
|
+
export function createTunnelTcpClientWebSocket(param) {
|
|
1211
|
+
let helper = createTunnelTcpClientHelper({
|
|
1212
|
+
serverKey: param.serverKey,
|
|
1213
|
+
uniqueId: 1,
|
|
1214
|
+
clientDataId: 0,
|
|
1215
|
+
signal: param.signal,
|
|
1216
|
+
})
|
|
1217
|
+
helper.writer = helper.writable.getWriter()
|
|
1218
|
+
let signal = Promise_withResolvers()
|
|
1219
|
+
/** @type{WritableStreamDefaultWriter<Uint8Array>} */
|
|
1220
|
+
let socketWriter = null
|
|
1221
|
+
helper.readable.pipeTo(new WritableStream({
|
|
1222
|
+
async write(chunk) {
|
|
1223
|
+
while (!param.signal.aborted && socketWriter == null) {
|
|
1224
|
+
await signal.promise
|
|
1225
|
+
}
|
|
1226
|
+
if (!param.signal.aborted) {
|
|
1227
|
+
await socketWriter.write(chunk)
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}))
|
|
1231
|
+
|
|
1232
|
+
async function connectWebSocket() {
|
|
1233
|
+
let promise = Promise_withResolvers()
|
|
1234
|
+
const ws = new WebSocket(param.url)
|
|
1235
|
+
ws.addEventListener('open', () => {
|
|
1236
|
+
socketWriter = new WritableStream({
|
|
1237
|
+
async write(chunk) {
|
|
1238
|
+
ws.send(chunk)
|
|
1239
|
+
}
|
|
1240
|
+
}).getWriter()
|
|
1241
|
+
ws.addEventListener('message', async (ev) => {
|
|
1242
|
+
let buffer = await ev.data.arrayBuffer()
|
|
1243
|
+
await helper.writer.write(new Uint8Array(buffer))
|
|
1244
|
+
})
|
|
1245
|
+
signal.resolve()
|
|
1246
|
+
})
|
|
1247
|
+
ws.addEventListener('error', (ev) => {
|
|
1248
|
+
console.error('createTunnelTcpClientWebSocket connectWebSocket on error')
|
|
1249
|
+
promise.resolve()
|
|
1250
|
+
})
|
|
1251
|
+
ws.addEventListener('close', (ev) => {
|
|
1252
|
+
console.info('createTunnelTcpClientWebSocket connectWebSocket on close')
|
|
1253
|
+
promise.resolve()
|
|
1254
|
+
})
|
|
1255
|
+
const listenerAC = () => { ws.close() }
|
|
1256
|
+
param.signal.addEventListener('abort', listenerAC)
|
|
1257
|
+
await promise.promise
|
|
1258
|
+
param.signal.removeEventListener('abort', listenerAC)
|
|
1259
|
+
socketWriter = null
|
|
1260
|
+
signal.resolve()
|
|
1261
|
+
signal = Promise_withResolvers()
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
timeWaitRetryLoop(param.signal, async () => {
|
|
1265
|
+
console.info('createTunnelTcpClientWebSocket timeWaitRetryLoop', 'connectWebSocket')
|
|
1266
|
+
await connectWebSocket()
|
|
1267
|
+
})
|
|
1268
|
+
|
|
1269
|
+
return helper
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
/**
|
|
1273
|
+
* @param {{
|
|
1274
|
+
* path:string;
|
|
1275
|
+
* router:Router<any, {}>;
|
|
1276
|
+
* signal:AbortSignal;
|
|
1277
|
+
* serverKey?:string;
|
|
1278
|
+
* }} param
|
|
1279
|
+
*/
|
|
1280
|
+
export function createTunnelTcpServerKoaRouter(param) {
|
|
1281
|
+
/** @type{TunnelTcpServerHelperParam} */
|
|
1282
|
+
let helperParam = {
|
|
1283
|
+
serverKey: param.serverKey,
|
|
1284
|
+
uniqueId: 1,
|
|
1285
|
+
listenMap: new Map(),
|
|
1286
|
+
dstMap: new Map(),
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
param.router.post(param.path, async (ctx) => {
|
|
1290
|
+
console.info('clientId:', 'createTunnelTcpServerKoaRouter on post ' + param.path)
|
|
1291
|
+
let helper = createTunnelTcpServerHelper(helperParam)
|
|
1292
|
+
helper.writer = helper.writable.getWriter()
|
|
1293
|
+
helper.reader = helper.readable.getReader()
|
|
1294
|
+
ctx.req.on('error', (e) => { console.error('createTunnelTcpServerKoaRouter in req error', e.message) })
|
|
1295
|
+
Readable.toWeb(ctx.req).pipeTo(new WritableStream({
|
|
1296
|
+
async write(chunk) {
|
|
1297
|
+
await helper.writer.write(chunk)
|
|
1298
|
+
}
|
|
1299
|
+
})).catch((err) => { console.error('web stream error', err.message) })
|
|
1300
|
+
|
|
1301
|
+
ctx.status = 200
|
|
1302
|
+
ctx.response.set({
|
|
1303
|
+
'Cache-Control': 'no-cache',
|
|
1304
|
+
'Content-Type': 'application/octet-stream'
|
|
1305
|
+
})
|
|
1306
|
+
ctx.body = Readable.fromWeb(new ReadableStream({
|
|
1307
|
+
async pull(controller) {
|
|
1308
|
+
let o = await helper.reader.read()
|
|
1309
|
+
controller.enqueue(o.value)
|
|
1310
|
+
},
|
|
1311
|
+
cancel() {
|
|
1312
|
+
helperParam.dstMap.delete(helper.dstId)
|
|
1313
|
+
}
|
|
1314
|
+
})).on('error', (err) => { console.error('web stream error', err.message) })
|
|
1315
|
+
})
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* @param {string} url
|
|
1320
|
+
* @returns {http}
|
|
1321
|
+
*/
|
|
1322
|
+
export function getHttpx(url) {
|
|
1323
|
+
/** @type{*} */
|
|
1324
|
+
let httpx = http
|
|
1325
|
+
if (url.startsWith('https:')) {
|
|
1326
|
+
httpx = https
|
|
1327
|
+
}
|
|
1328
|
+
return httpx
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* @param {{
|
|
1333
|
+
* signal:AbortSignal;
|
|
1334
|
+
* serverKey?:string;
|
|
1335
|
+
* url:string;
|
|
1336
|
+
* timeout?:number;
|
|
1337
|
+
* oncreateoutconnect?:()=>{};
|
|
1338
|
+
* headersFn?:()=>Promise<object>;
|
|
1339
|
+
* }} param
|
|
1340
|
+
*/
|
|
1341
|
+
export function createTunnelTcpClientHttp(param) {
|
|
1342
|
+
let helper = createTunnelTcpClientHelper({
|
|
1343
|
+
serverKey: param.serverKey,
|
|
1344
|
+
signal: param.signal,
|
|
1345
|
+
uniqueId: 1,
|
|
1346
|
+
clientDataId: 0,
|
|
1347
|
+
})
|
|
1348
|
+
helper.writer = helper.writable.getWriter()
|
|
1349
|
+
let signal = Promise_withResolvers()
|
|
1350
|
+
/** @type{WritableStreamDefaultWriter<Uint8Array>} */
|
|
1351
|
+
let socketWriter = null
|
|
1352
|
+
let bufferedTransform = createTimeBufferedTransformStream(50)
|
|
1353
|
+
helper.readable.pipeThrough(bufferedTransform).pipeTo(new WritableStream({
|
|
1354
|
+
async write(chunk) {
|
|
1355
|
+
while (!param.signal.aborted && socketWriter == null) {
|
|
1356
|
+
await signal.promise
|
|
1357
|
+
}
|
|
1358
|
+
if (!param.signal.aborted) {
|
|
1359
|
+
await socketWriter.write(chunk)
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
})).catch((err) => { console.error('web stream error', err.message) })
|
|
1363
|
+
|
|
1364
|
+
/** @type{Set<()=>void>} */
|
|
1365
|
+
const abortListenerSet = new Set()
|
|
1366
|
+
param.signal.addEventListener('abort', () => {
|
|
1367
|
+
abortListenerSet.forEach(o => o())
|
|
1368
|
+
})
|
|
1369
|
+
async function createConnectionHttpx() {
|
|
1370
|
+
console.info('createTunnelTcpClientHttpV2 createConnectionHttpx')
|
|
1371
|
+
let addHeaders = {}
|
|
1372
|
+
if (param.headersFn) {
|
|
1373
|
+
addHeaders = await param.headersFn()
|
|
1374
|
+
}
|
|
1375
|
+
const ac = new AbortController()
|
|
1376
|
+
const listenerAC = () => { ac.abort() }
|
|
1377
|
+
let promise = Promise_withResolvers()
|
|
1378
|
+
let transform = new TransformStream()
|
|
1379
|
+
socketWriter = transform.writable.getWriter()
|
|
1380
|
+
signal.resolve()
|
|
1381
|
+
abortListenerSet.add(listenerAC)
|
|
1382
|
+
let httpx = getHttpx(param.url)
|
|
1383
|
+
let req = httpx.request(param.url, {
|
|
1384
|
+
method: 'POST',
|
|
1385
|
+
signal: ac.signal,
|
|
1386
|
+
timeout: param.timeout ?? 24 * 3600 * 1000,
|
|
1387
|
+
headers: {
|
|
1388
|
+
'Content-Type': 'application/octet-stream',
|
|
1389
|
+
...addHeaders,
|
|
1390
|
+
},
|
|
1391
|
+
}, (res) => {
|
|
1392
|
+
param.oncreateoutconnect && param.oncreateoutconnect()
|
|
1393
|
+
Readable.toWeb(res).pipeTo(new WritableStream({
|
|
1394
|
+
async write(chunk) {
|
|
1395
|
+
await helper.writer.write(chunk)
|
|
1396
|
+
}
|
|
1397
|
+
})).catch((err) => {
|
|
1398
|
+
console.error('web stream error', err.message)
|
|
1399
|
+
})
|
|
1400
|
+
res.on('error', (e) => {
|
|
1401
|
+
console.error('createConnectionHttpx res error ', e.message)
|
|
1402
|
+
promise.resolve()
|
|
1403
|
+
})
|
|
1404
|
+
res.on('close', () => {
|
|
1405
|
+
console.error('createConnectionHttpx res close ')
|
|
1406
|
+
promise.resolve()
|
|
1407
|
+
})
|
|
1408
|
+
setTimeout(() => {
|
|
1409
|
+
if (helper.param.clientDataId < 1) {
|
|
1410
|
+
res.destroy(new Error('init clientId failed!'))
|
|
1411
|
+
}
|
|
1412
|
+
}, 10_000).unref()
|
|
1413
|
+
})
|
|
1414
|
+
Readable.fromWeb(transform.readable).pipe(req)
|
|
1415
|
+
req.flushHeaders()
|
|
1416
|
+
req.on('error', (e) => {
|
|
1417
|
+
console.error('createConnectionHttpx', e.message)
|
|
1418
|
+
promise.resolve()
|
|
1419
|
+
})
|
|
1420
|
+
req.on('close', () => {
|
|
1421
|
+
console.error('createConnectionHttpx req close')
|
|
1422
|
+
promise.resolve()
|
|
1423
|
+
})
|
|
1424
|
+
await promise.promise
|
|
1425
|
+
abortListenerSet.delete(listenerAC)
|
|
1426
|
+
socketWriter = null
|
|
1427
|
+
signal.resolve()
|
|
1428
|
+
signal = Promise_withResolvers()
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
timeWaitRetryLoop(param.signal, async () => {
|
|
1432
|
+
await createConnectionHttpx()
|
|
1433
|
+
})
|
|
1434
|
+
|
|
1435
|
+
return helper
|
|
1543
1436
|
}
|