js-tcp-tunnel 1.0.2 → 1.1.0
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 +5 -5
- package/src/lib.js +73 -159
- package/src/lib.test.js +144 -3
- package/src/types.ts +114 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-tcp-tunnel",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "js tcp tunnel",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://github.com/yuanliwei/js-tunnel#readme",
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"
|
|
29
|
-
"koa
|
|
27
|
+
"@koa/router": "^14.0.0",
|
|
28
|
+
"@types/node": "^24.2.1",
|
|
29
|
+
"koa": "^3.0.1",
|
|
30
30
|
"log4js": "^6.9.1",
|
|
31
|
-
"ws": "^8.18.
|
|
31
|
+
"ws": "^8.18.3"
|
|
32
32
|
}
|
|
33
33
|
}
|
package/src/lib.js
CHANGED
|
@@ -3,10 +3,12 @@ import net from 'node:net'
|
|
|
3
3
|
import { Readable, Writable } from 'node:stream'
|
|
4
4
|
import http from 'node:http'
|
|
5
5
|
import https from 'node:https'
|
|
6
|
+
import { ReadableStream, TransformStream, WritableStream } from 'node:stream/web'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @import {WebSocketServer} from 'ws'
|
|
9
|
-
* @import Router from 'koa
|
|
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'
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
const DEBUG_TUNNEL_TCP = false
|
|
@@ -53,14 +55,6 @@ export function formatNumber(num) {
|
|
|
53
55
|
return Number(num.toFixed(2))
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
/**
|
|
57
|
-
* @typedef {{
|
|
58
|
-
* promise: Promise<any>;
|
|
59
|
-
* resolve: (value: any | null) => void;
|
|
60
|
-
* reject: (reason: any | null) => void;
|
|
61
|
-
* }} PromiseResolvers
|
|
62
|
-
*/
|
|
63
|
-
|
|
64
58
|
export function Promise_withResolvers() {
|
|
65
59
|
/** @type{(value?:object)=>void} */
|
|
66
60
|
let resolve = null
|
|
@@ -74,8 +68,8 @@ export function Promise_withResolvers() {
|
|
|
74
68
|
}
|
|
75
69
|
|
|
76
70
|
/**
|
|
77
|
-
* @param {Promise<[CryptoKey,Uint8Array]>} key_iv
|
|
78
|
-
* @returns {TransformStream<Uint8Array
|
|
71
|
+
* @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
|
|
72
|
+
* @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
|
|
79
73
|
*/
|
|
80
74
|
export function createEncodeStream(key_iv) {
|
|
81
75
|
let key = null
|
|
@@ -92,8 +86,8 @@ export function createEncodeStream(key_iv) {
|
|
|
92
86
|
}
|
|
93
87
|
|
|
94
88
|
/**
|
|
95
|
-
* @param {Promise<[CryptoKey,Uint8Array]>} key_iv
|
|
96
|
-
* @returns {TransformStream<Uint8Array
|
|
89
|
+
* @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
|
|
90
|
+
* @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
|
|
97
91
|
*/
|
|
98
92
|
export function createDecodeStream(key_iv) {
|
|
99
93
|
let key = null
|
|
@@ -116,10 +110,10 @@ export function createDecodeStream(key_iv) {
|
|
|
116
110
|
const HEADER_CHECK = 0xb1f7705f
|
|
117
111
|
|
|
118
112
|
/**
|
|
119
|
-
* @param {Uint8Array[]} queue
|
|
113
|
+
* @param {Uint8Array<ArrayBuffer>[]} queue
|
|
120
114
|
* @param {CryptoKey} key
|
|
121
|
-
* @param {Uint8Array} iv
|
|
122
|
-
* @returns {Promise<Uint8Array
|
|
115
|
+
* @param {Uint8Array<ArrayBuffer>} iv
|
|
116
|
+
* @returns {Promise<Uint8Array<ArrayBuffer>>}
|
|
123
117
|
*/
|
|
124
118
|
export async function buildBufferData(queue, key, iv) {
|
|
125
119
|
let buffers = []
|
|
@@ -138,11 +132,11 @@ export async function buildBufferData(queue, key, iv) {
|
|
|
138
132
|
/**
|
|
139
133
|
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
140
134
|
* @param {CryptoKey} key
|
|
141
|
-
* @param {Uint8Array} iv
|
|
142
|
-
* @returns {Promise<[Uint8Array[],Uint8Array<ArrayBuffer>]>}
|
|
135
|
+
* @param {Uint8Array<ArrayBuffer>} iv
|
|
136
|
+
* @returns {Promise<[Uint8Array<ArrayBuffer>[],Uint8Array<ArrayBuffer>]>}
|
|
143
137
|
*/
|
|
144
138
|
export async function parseBufferData(buffer, key, iv) {
|
|
145
|
-
/** @type{Uint8Array[]} */
|
|
139
|
+
/** @type{Uint8Array<ArrayBuffer>[]} */
|
|
146
140
|
let queue = []
|
|
147
141
|
let offset = 0
|
|
148
142
|
let remain = new Uint8Array(0)
|
|
@@ -303,17 +297,17 @@ let tcpTunnelDataSend = 0
|
|
|
303
297
|
*/
|
|
304
298
|
export function printTcpTunnelData(data) {
|
|
305
299
|
return `id: ${`${data.srcId}:${data.dstId}`.padEnd(10)} channel: ${`${data.srcChannel}:${data.dstChannel}`.padEnd(10)} ${{
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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 ',
|
|
317
311
|
}[data.type]} recv: ${(tcpTunnelDataRecv)} send: ${(tcpTunnelDataSend)} size:${data.buffer.length}`
|
|
318
312
|
}
|
|
319
313
|
|
|
@@ -321,7 +315,7 @@ export function printTcpTunnelData(data) {
|
|
|
321
315
|
*
|
|
322
316
|
* @param {string} password
|
|
323
317
|
* @param {number} iterations
|
|
324
|
-
* @returns {Promise<[CryptoKey,Uint8Array]>}
|
|
318
|
+
* @returns {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>}
|
|
325
319
|
*/
|
|
326
320
|
export async function buildKeyIv(password, iterations) {
|
|
327
321
|
if (!TUNNEL_TCP_WITH_CRYPTO) return [null, null]
|
|
@@ -357,9 +351,9 @@ export async function buildKeyIv(password, iterations) {
|
|
|
357
351
|
|
|
358
352
|
/**
|
|
359
353
|
*
|
|
360
|
-
* @param {Uint8Array} data
|
|
354
|
+
* @param {Uint8Array<ArrayBuffer>} data
|
|
361
355
|
* @param {CryptoKey} key
|
|
362
|
-
* @param {Uint8Array} iv
|
|
356
|
+
* @param {Uint8Array<ArrayBuffer>} iv
|
|
363
357
|
* @returns
|
|
364
358
|
*/
|
|
365
359
|
export async function encrypt(data, key, iv) {
|
|
@@ -372,9 +366,9 @@ export async function encrypt(data, key, iv) {
|
|
|
372
366
|
}
|
|
373
367
|
|
|
374
368
|
/**
|
|
375
|
-
* @param {Uint8Array} data
|
|
369
|
+
* @param {Uint8Array<ArrayBuffer>} data
|
|
376
370
|
* @param {CryptoKey} key
|
|
377
|
-
* @param {Uint8Array} iv
|
|
371
|
+
* @param {Uint8Array<ArrayBuffer>} iv
|
|
378
372
|
* @returns
|
|
379
373
|
*/
|
|
380
374
|
export async function decrypt(data, key, iv) {
|
|
@@ -428,83 +422,6 @@ export const TUNNEL_TCP_TYPE_PING = 0x4768e1ba
|
|
|
428
422
|
export const TUNNEL_TCP_TYPE_PONG = 0x106f43fb
|
|
429
423
|
export const TUNNEL_TCP_TYPE_ACK = 0xc5870539
|
|
430
424
|
|
|
431
|
-
/**
|
|
432
|
-
* @typedef {TUNNEL_TCP_TYPE_INIT
|
|
433
|
-
* |TUNNEL_TCP_TYPE_LISTEN
|
|
434
|
-
* |TUNNEL_TCP_TYPE_ONLISTEN
|
|
435
|
-
* |TUNNEL_TCP_TYPE_CONNECT
|
|
436
|
-
* |TUNNEL_TCP_TYPE_ONCONNECT
|
|
437
|
-
* |TUNNEL_TCP_TYPE_DATA
|
|
438
|
-
* |TUNNEL_TCP_TYPE_ERROR
|
|
439
|
-
* |TUNNEL_TCP_TYPE_CLOSE
|
|
440
|
-
* |TUNNEL_TCP_TYPE_PING
|
|
441
|
-
* |TUNNEL_TCP_TYPE_PONG
|
|
442
|
-
* |TUNNEL_TCP_TYPE_ACK} TUNNEL_TCP_TYPE
|
|
443
|
-
*/
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* @typedef {{
|
|
447
|
-
* type:TUNNEL_TCP_TYPE;
|
|
448
|
-
* srcId:number;
|
|
449
|
-
* dstId:number;
|
|
450
|
-
* srcChannel:number;
|
|
451
|
-
* dstChannel:number;
|
|
452
|
-
* buffer:Uint8Array;
|
|
453
|
-
* }} TCP_TUNNEL_DATA
|
|
454
|
-
*/
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* @typedef {{
|
|
458
|
-
* key:string;
|
|
459
|
-
* }} TUNNEL_TCP_DATA_LISTEN
|
|
460
|
-
*/
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* @typedef {{
|
|
464
|
-
* key:string;
|
|
465
|
-
* }} TUNNEL_TCP_DATA_CONNECT
|
|
466
|
-
*/
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* @typedef {{
|
|
470
|
-
* time:number;
|
|
471
|
-
* }} TUNNEL_TCP_DATA_PINGPONG
|
|
472
|
-
*/
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* @typedef {{
|
|
476
|
-
* id:number;
|
|
477
|
-
* encodeWriter:WritableStreamDefaultWriter<Uint8Array>;
|
|
478
|
-
* }} TUNNEL_TCP_SERVER
|
|
479
|
-
*/
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* @typedef {{
|
|
483
|
-
* readable:ReadableStream<Uint8Array>;
|
|
484
|
-
* writable:WritableStream<Uint8Array>;
|
|
485
|
-
* reader:ReadableStreamDefaultReader<Uint8Array>;
|
|
486
|
-
* writer:WritableStreamDefaultWriter<Uint8Array>;
|
|
487
|
-
* dstId:number;
|
|
488
|
-
* }} TUNNEL_TCP_SERVER_HELPER
|
|
489
|
-
* @typedef {{
|
|
490
|
-
* readable:ReadableStream<Uint8Array>;
|
|
491
|
-
* writable:WritableStream<Uint8Array>;
|
|
492
|
-
* reader:ReadableStreamDefaultReader<Uint8Array>;
|
|
493
|
-
* writer:WritableStreamDefaultWriter<Uint8Array>;
|
|
494
|
-
* listen:(param:{
|
|
495
|
-
* clientKey?:string;
|
|
496
|
-
* tunnelKey:string;
|
|
497
|
-
* host?:string;
|
|
498
|
-
* port:number;
|
|
499
|
-
* })=>Promise<void>;
|
|
500
|
-
* connect:(param:{
|
|
501
|
-
* clientKey?:string;
|
|
502
|
-
* tunnelKey: string;
|
|
503
|
-
* port: number;
|
|
504
|
-
* })=>Promise<void>;
|
|
505
|
-
* }} TUNNEL_TCP_CLIENT_HELPER
|
|
506
|
-
*/
|
|
507
|
-
|
|
508
425
|
/**
|
|
509
426
|
* @param {TCP_TUNNEL_DATA} box
|
|
510
427
|
*/
|
|
@@ -520,7 +437,7 @@ export function buildTcpTunnelData(box) {
|
|
|
520
437
|
}
|
|
521
438
|
|
|
522
439
|
/**
|
|
523
|
-
* @param {Uint8Array} buffer
|
|
440
|
+
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
524
441
|
*/
|
|
525
442
|
export function parseTcpTunnelData(buffer) {
|
|
526
443
|
let offset = 0
|
|
@@ -617,25 +534,43 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
|
|
|
617
534
|
let signal = Promise_withResolvers()
|
|
618
535
|
signal.resolve()
|
|
619
536
|
let sendPackSize = 0
|
|
620
|
-
let
|
|
537
|
+
let remoteRecvPackSize = 0
|
|
621
538
|
channel.notify = (size) => {
|
|
622
|
-
|
|
539
|
+
remoteRecvPackSize = size
|
|
623
540
|
signal.resolve()
|
|
624
541
|
}
|
|
625
542
|
let [clientKey, clientIv] = channel.key_iv
|
|
626
543
|
let bufferedTransform = createTimeBufferedTransformStream(50)
|
|
544
|
+
let backPressureTimer = null
|
|
627
545
|
Readable.toWeb(socket).pipeThrough(bufferedTransform).pipeTo(new WritableStream({
|
|
546
|
+
/**
|
|
547
|
+
* @param {Uint8Array<ArrayBuffer>} chunk
|
|
548
|
+
*/
|
|
628
549
|
async write(chunk) {
|
|
629
550
|
const buffer = await encrypt(chunk, clientKey, clientIv)
|
|
630
|
-
let bufferPackSize = sendPackSize -
|
|
551
|
+
let bufferPackSize = sendPackSize - remoteRecvPackSize
|
|
552
|
+
if (DEBUG_TUNNEL_TCP) {
|
|
553
|
+
console.warn('bufferPackSize:', bufferPackSize)
|
|
554
|
+
}
|
|
631
555
|
if (bufferPackSize > 10) {
|
|
632
556
|
signal.resolve()
|
|
633
557
|
signal = Promise_withResolvers()
|
|
558
|
+
const s = signal
|
|
559
|
+
backPressureTimer = setTimeout(() => {
|
|
560
|
+
s.resolve()
|
|
561
|
+
console.error('pipeSocketDataWithChannel timeout close channel')
|
|
562
|
+
sendPackSize = 0
|
|
563
|
+
this.close()
|
|
564
|
+
}, 10_000).unref()
|
|
634
565
|
if (DEBUG_TUNNEL_TCP) {
|
|
635
|
-
console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:',
|
|
566
|
+
console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:', remoteRecvPackSize, ' bufferPackSize:', bufferPackSize)
|
|
636
567
|
}
|
|
637
568
|
}
|
|
638
569
|
await signal.promise
|
|
570
|
+
if (backPressureTimer) {
|
|
571
|
+
clearTimeout(backPressureTimer)
|
|
572
|
+
backPressureTimer = null
|
|
573
|
+
}
|
|
639
574
|
await encodeWriter.write(buildTcpTunnelData({
|
|
640
575
|
type: TUNNEL_TCP_TYPE_DATA,
|
|
641
576
|
srcId: channel.srcId,
|
|
@@ -647,7 +582,7 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
|
|
|
647
582
|
sendPackSize++
|
|
648
583
|
},
|
|
649
584
|
async close() {
|
|
650
|
-
|
|
585
|
+
encodeWriter.write(buildTcpTunnelData({
|
|
651
586
|
type: TUNNEL_TCP_TYPE_CLOSE,
|
|
652
587
|
srcId: channel.srcId,
|
|
653
588
|
srcChannel: channel.srcChannel,
|
|
@@ -656,6 +591,7 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
|
|
|
656
591
|
buffer: new Uint8Array(0),
|
|
657
592
|
})).catch((err) => { console.error('web stream write error', err.message) })
|
|
658
593
|
channelMap.delete(channelId)
|
|
594
|
+
socket.destroy()
|
|
659
595
|
}
|
|
660
596
|
})).catch((err) => {
|
|
661
597
|
console.error('web stream error', err.message)
|
|
@@ -694,8 +630,8 @@ function natTunnelData(param, data) {
|
|
|
694
630
|
|
|
695
631
|
/**
|
|
696
632
|
* @param {TunnelTcpServerHelperParam} param
|
|
697
|
-
* @param {WritableStreamDefaultWriter<Uint8Array<
|
|
698
|
-
* @param {Uint8Array<
|
|
633
|
+
* @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} encodeWriter
|
|
634
|
+
* @param {Uint8Array<ArrayBuffer>} chunk
|
|
699
635
|
*/
|
|
700
636
|
async function dispatchServerBufferData(param, encodeWriter, chunk) {
|
|
701
637
|
let data = parseTcpTunnelData(chunk)
|
|
@@ -741,10 +677,10 @@ async function dispatchServerBufferData(param, encodeWriter, chunk) {
|
|
|
741
677
|
/**
|
|
742
678
|
* @param {TunnelTcpClientHelperParam} param
|
|
743
679
|
* @param {{ (): Promise<void>; }} setup
|
|
744
|
-
* @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} listenKeyParamMap
|
|
680
|
+
* @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} listenKeyParamMap
|
|
745
681
|
* @param {Map<number, SocketChannel>} channelMap
|
|
746
|
-
* @param {WritableStreamDefaultWriter<Uint8Array<
|
|
747
|
-
* @param {Uint8Array<
|
|
682
|
+
* @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} encodeWriter
|
|
683
|
+
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
748
684
|
*/
|
|
749
685
|
async function dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer) {
|
|
750
686
|
let data = parseTcpTunnelData(buffer)
|
|
@@ -846,7 +782,7 @@ async function dispatchClientBufferData(param, setup, listenKeyParamMap, channel
|
|
|
846
782
|
let channel = channelMap.get(channelId)
|
|
847
783
|
if (channel) {
|
|
848
784
|
channelMap.delete(channelId)
|
|
849
|
-
channel.socket.
|
|
785
|
+
channel.socket.destroy()
|
|
850
786
|
}
|
|
851
787
|
}
|
|
852
788
|
if (data.type == TUNNEL_TCP_TYPE_ERROR) {
|
|
@@ -923,39 +859,6 @@ export async function closeRemoteChannel(encodeWriter, data) {
|
|
|
923
859
|
})).catch((err) => { console.error('closeRemoteChannel stream write error', err.message) })
|
|
924
860
|
}
|
|
925
861
|
|
|
926
|
-
/**
|
|
927
|
-
* @typedef {{
|
|
928
|
-
* writer:WritableStreamDefaultWriter<Uint8Array>;
|
|
929
|
-
* socket:net.Socket;
|
|
930
|
-
* srcId:number;
|
|
931
|
-
* dstId:number;
|
|
932
|
-
* srcChannel:number;
|
|
933
|
-
* dstChannel:number;
|
|
934
|
-
* notify:(size:number)=>void;
|
|
935
|
-
* recvPackSize:number;
|
|
936
|
-
* key_iv:[CryptoKey, Uint8Array];
|
|
937
|
-
* }} SocketChannel
|
|
938
|
-
*/
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
/**
|
|
942
|
-
* @typedef {{
|
|
943
|
-
* uniqueId:number;
|
|
944
|
-
* listenMap:Map<string,number>;
|
|
945
|
-
* dstMap:Map<number,TUNNEL_TCP_SERVER>;
|
|
946
|
-
* serverKey?:string;
|
|
947
|
-
* }} TunnelTcpServerHelperParam
|
|
948
|
-
*/
|
|
949
|
-
|
|
950
|
-
/**
|
|
951
|
-
* @typedef {{
|
|
952
|
-
* signal:AbortSignal;
|
|
953
|
-
* serverKey?:string;
|
|
954
|
-
* uniqueId:number;
|
|
955
|
-
* clientDataId:number;
|
|
956
|
-
* }} TunnelTcpClientHelperParam
|
|
957
|
-
*/
|
|
958
|
-
|
|
959
862
|
/**
|
|
960
863
|
* @param {TunnelTcpServerHelperParam} param
|
|
961
864
|
*/
|
|
@@ -1010,7 +913,7 @@ export function createTunnelTcpClientHelper(param) {
|
|
|
1010
913
|
/** @type{Map<number,SocketChannel>} */
|
|
1011
914
|
let channelMap = new Map()
|
|
1012
915
|
|
|
1013
|
-
/** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} */
|
|
916
|
+
/** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} */
|
|
1014
917
|
let listenKeyParamMap = new Map()
|
|
1015
918
|
|
|
1016
919
|
let server_key_iv = buildKeyIv(param.serverKey, 10)
|
|
@@ -1037,6 +940,9 @@ export function createTunnelTcpClientHelper(param) {
|
|
|
1037
940
|
}
|
|
1038
941
|
|
|
1039
942
|
decode.readable.pipeTo(new WritableStream({
|
|
943
|
+
/**
|
|
944
|
+
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
945
|
+
*/
|
|
1040
946
|
async write(buffer) {
|
|
1041
947
|
try {
|
|
1042
948
|
await dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer)
|
|
@@ -1144,7 +1050,7 @@ export function createTunnelTcpClientHelper(param) {
|
|
|
1144
1050
|
}
|
|
1145
1051
|
|
|
1146
1052
|
/** @type{TUNNEL_TCP_CLIENT_HELPER} */
|
|
1147
|
-
let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, listen, connect }
|
|
1053
|
+
let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, listen, connect, param: outParam }
|
|
1148
1054
|
return helper
|
|
1149
1055
|
|
|
1150
1056
|
}
|
|
@@ -1467,6 +1373,9 @@ export function createTunnelTcpClientHttp(param) {
|
|
|
1467
1373
|
let socketWriter = null
|
|
1468
1374
|
let bufferedTransform = createTimeBufferedTransformStream(50)
|
|
1469
1375
|
helper.readable.pipeThrough(bufferedTransform).pipeTo(new WritableStream({
|
|
1376
|
+
/**
|
|
1377
|
+
* @param {Uint8Array<ArrayBufferLike>} chunk
|
|
1378
|
+
*/
|
|
1470
1379
|
async write(chunk) {
|
|
1471
1380
|
while (!param.signal.aborted && socketWriter == null) {
|
|
1472
1381
|
await signal.promise
|
|
@@ -1521,6 +1430,11 @@ export function createTunnelTcpClientHttp(param) {
|
|
|
1521
1430
|
console.error('createConnectionHttpx res close ')
|
|
1522
1431
|
promise.resolve()
|
|
1523
1432
|
})
|
|
1433
|
+
setTimeout(() => {
|
|
1434
|
+
if (helper.param.clientDataId < 1) {
|
|
1435
|
+
res.destroy(new Error('init clientId failed!'))
|
|
1436
|
+
}
|
|
1437
|
+
}, 10_000).unref()
|
|
1524
1438
|
})
|
|
1525
1439
|
Readable.fromWeb(transform.readable).pipe(req)
|
|
1526
1440
|
req.flushHeaders()
|
package/src/lib.test.js
CHANGED
|
@@ -5,7 +5,7 @@ import net from 'net'
|
|
|
5
5
|
import http from 'http'
|
|
6
6
|
import { Readable, Transform } from 'node:stream'
|
|
7
7
|
import Koa from 'koa'
|
|
8
|
-
import Router from 'koa
|
|
8
|
+
import Router from '@koa/router'
|
|
9
9
|
import { WebSocketServer } from 'ws'
|
|
10
10
|
import log4js from 'log4js'
|
|
11
11
|
|
|
@@ -62,7 +62,7 @@ test('tunnel-tcp-socket-test', async () => {
|
|
|
62
62
|
console.error(err.message)
|
|
63
63
|
})
|
|
64
64
|
|
|
65
|
-
let rec =
|
|
65
|
+
let rec = ''
|
|
66
66
|
|
|
67
67
|
await new Promise((resolve) => {
|
|
68
68
|
socket.on('connect', async () => {
|
|
@@ -86,7 +86,8 @@ test('tunnel-tcp-socket-test', async () => {
|
|
|
86
86
|
socketServer.close()
|
|
87
87
|
})
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
let from = rec.length - 'qwertyuiop - 99'.length
|
|
90
|
+
strictEqual(rec.substring(from), 'qwertyuiop - 99')
|
|
90
91
|
|
|
91
92
|
})
|
|
92
93
|
console.info('over!')
|
|
@@ -508,4 +509,144 @@ test('test-connect', { skip: SKIP_MANAUAL_TEST }, async () => {
|
|
|
508
509
|
|
|
509
510
|
await sleep(10000_000)
|
|
510
511
|
})
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
test('terminal-server', async () => {
|
|
515
|
+
// node --test-name-pattern="^terminal-server$" src/lib.test.js
|
|
516
|
+
using stack = new DisposableStack()
|
|
517
|
+
const ac = new AbortController()
|
|
518
|
+
stack.adopt(ac, () => { ac.abort() })
|
|
519
|
+
let transformSize = 0
|
|
520
|
+
|
|
521
|
+
async function createEchoServer() {
|
|
522
|
+
console.info('创建socket服务')
|
|
523
|
+
let socketServer = net.createServer((socket) => {
|
|
524
|
+
socket.pipe(new Transform({
|
|
525
|
+
transform(chunk, _, callback) {
|
|
526
|
+
// console.info('transform chunk', chunk.length)
|
|
527
|
+
transformSize += chunk.length
|
|
528
|
+
this.push("echo::::")
|
|
529
|
+
this.push(chunk)
|
|
530
|
+
callback()
|
|
531
|
+
}
|
|
532
|
+
})).pipe(socket).on('error', (err) => console.error(err.message))
|
|
533
|
+
}).listen(9036)
|
|
534
|
+
await sleep(100)
|
|
535
|
+
socketServer.on('error', (err) => {
|
|
536
|
+
console.error(err.message)
|
|
537
|
+
})
|
|
538
|
+
ac.signal.addEventListener('abort', () => {
|
|
539
|
+
socketServer.close()
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
*
|
|
545
|
+
* @param {AbortController} ac
|
|
546
|
+
*/
|
|
547
|
+
async function createDispatchServer(ac) {
|
|
548
|
+
console.info('创建转发服务 http')
|
|
549
|
+
let app = new Koa()
|
|
550
|
+
let router = new Router()
|
|
551
|
+
createTunnelTcpServerKoaRouter({
|
|
552
|
+
signal: ac.signal,
|
|
553
|
+
router: router,
|
|
554
|
+
path: '/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
|
|
555
|
+
serverKey: '2934c57f790f9e99a52a121802df231c',
|
|
556
|
+
})
|
|
557
|
+
app.use(router.routes())
|
|
558
|
+
app.use(router.allowedMethods())
|
|
559
|
+
app.onerror = (err) => console.info(err.message)
|
|
560
|
+
let koaServer = http.createServer(app.callback())
|
|
561
|
+
koaServer.listen(9035)
|
|
562
|
+
koaServer.on('error', (e) => { console.error(e.message) })
|
|
563
|
+
ac.signal.addEventListener('abort', () => {
|
|
564
|
+
console.info('close koa server')
|
|
565
|
+
koaServer.close((err) => {
|
|
566
|
+
console.info('on server close result ', err)
|
|
567
|
+
})
|
|
568
|
+
koaServer.closeAllConnections()
|
|
569
|
+
})
|
|
570
|
+
await sleep(1000)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async function createListenClient() {
|
|
574
|
+
console.info('创建监听服务 http')
|
|
575
|
+
let connection1 = createTunnelTcpClientHttp({
|
|
576
|
+
url: 'http://127.0.0.1:9035/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
|
|
577
|
+
signal: ac.signal,
|
|
578
|
+
serverKey: '2934c57f790f9e99a52a121802df231c',
|
|
579
|
+
})
|
|
580
|
+
connection1.listen({
|
|
581
|
+
clientKey: 'mmm',
|
|
582
|
+
tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f',
|
|
583
|
+
host: '127.0.0.1',
|
|
584
|
+
port: 9036,
|
|
585
|
+
})
|
|
586
|
+
await sleep(100)
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
async function createConnectClient() {
|
|
590
|
+
console.info('创建连接服务 http 1')
|
|
591
|
+
let connection2 = createTunnelTcpClientHttp({
|
|
592
|
+
url: 'http://127.0.0.1:9035/tunnel/0f7c5b2c9080eaa9e4d6139126daac04',
|
|
593
|
+
signal: ac.signal,
|
|
594
|
+
serverKey: '2934c57f790f9e99a52a121802df231c',
|
|
595
|
+
})
|
|
596
|
+
connection2.connect({
|
|
597
|
+
clientKey: 'mmm',
|
|
598
|
+
tunnelKey: 'b0f5014acad2060d6bd3730a1721c97f',
|
|
599
|
+
port: 9037,
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
await sleep(1000)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
let socketCreateCount = 0
|
|
606
|
+
let recvCount = 0
|
|
607
|
+
async function startSendRecvTest() {
|
|
608
|
+
socketCreateCount++
|
|
609
|
+
console.info('tcp透传测试 start')
|
|
610
|
+
let socket1 = net.createConnection({ host: '127.0.0.1', port: 9037 })
|
|
611
|
+
socket1.on('error', (err) => { console.error(err.message) })
|
|
612
|
+
ac.signal.addEventListener('abort', () => { socket1.destroy() })
|
|
613
|
+
socket1.on('connect', async () => {
|
|
614
|
+
socket1.on('data', (chunk) => {
|
|
615
|
+
console.info('receive chunk ', Uint8Array_toString(chunk))
|
|
616
|
+
recvCount++
|
|
617
|
+
})
|
|
618
|
+
while (!ac.signal.aborted && !socket1.closed) {
|
|
619
|
+
await sleep(1000)
|
|
620
|
+
socket1.write(`iiiiiii>>>>>>> ${new Date().toLocaleString()}`)
|
|
621
|
+
}
|
|
622
|
+
console.info('finished socket.')
|
|
623
|
+
setTimeout(() => {
|
|
624
|
+
startSendRecvTest()
|
|
625
|
+
}, 1)
|
|
626
|
+
})
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
await createEchoServer()
|
|
630
|
+
let ac2 = new AbortController()
|
|
631
|
+
stack.adopt(0, () => ac2.abort())
|
|
632
|
+
await createDispatchServer(ac2)
|
|
633
|
+
await createListenClient()
|
|
634
|
+
await createConnectClient()
|
|
635
|
+
await startSendRecvTest()
|
|
636
|
+
|
|
637
|
+
await sleep(5000)
|
|
638
|
+
ac2.abort()
|
|
639
|
+
console.info('stop dispatch server')
|
|
640
|
+
await sleep(5_000)
|
|
641
|
+
ac2 = new AbortController()
|
|
642
|
+
stack.adopt(0, () => ac2.abort())
|
|
643
|
+
console.info('recreate dispatch server')
|
|
644
|
+
await createDispatchServer(ac2)
|
|
645
|
+
|
|
646
|
+
await sleep(20_000)
|
|
647
|
+
|
|
648
|
+
strictEqual(socketCreateCount, 2)
|
|
649
|
+
strictEqual(recvCount, 14)
|
|
650
|
+
|
|
651
|
+
console.info('over!')
|
|
511
652
|
})
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import net from 'node:net'
|
|
2
|
+
import { WritableStream, ReadableStream, ReadableStreamDefaultReader, WritableStreamDefaultWriter } from "stream/web"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export type PromiseResolvers = {
|
|
6
|
+
promise: Promise<any>
|
|
7
|
+
resolve: (value: any | null) => void
|
|
8
|
+
reject: (reason: any | null) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type TUNNEL_TCP_TYPE_INIT = 0xa9b398d5 // 链接建立后返回id
|
|
12
|
+
export type TUNNEL_TCP_TYPE_LISTEN = 0xe41957d3
|
|
13
|
+
export type TUNNEL_TCP_TYPE_ONLISTEN = 0x20993e38
|
|
14
|
+
export type TUNNEL_TCP_TYPE_CONNECT = 0x11d949f8
|
|
15
|
+
export type TUNNEL_TCP_TYPE_ONCONNECT = 0x377b2181
|
|
16
|
+
export type TUNNEL_TCP_TYPE_DATA = 0x48678f39
|
|
17
|
+
export type TUNNEL_TCP_TYPE_ERROR = 0x8117f762
|
|
18
|
+
export type TUNNEL_TCP_TYPE_CLOSE = 0x72fd6470
|
|
19
|
+
export type TUNNEL_TCP_TYPE_PING = 0x4768e1ba
|
|
20
|
+
export type TUNNEL_TCP_TYPE_PONG = 0x106f43fb
|
|
21
|
+
export type TUNNEL_TCP_TYPE_ACK = 0xc5870539
|
|
22
|
+
|
|
23
|
+
export type TUNNEL_TCP_TYPE = TUNNEL_TCP_TYPE_INIT |
|
|
24
|
+
TUNNEL_TCP_TYPE_LISTEN |
|
|
25
|
+
TUNNEL_TCP_TYPE_ONLISTEN |
|
|
26
|
+
TUNNEL_TCP_TYPE_CONNECT |
|
|
27
|
+
TUNNEL_TCP_TYPE_ONCONNECT |
|
|
28
|
+
TUNNEL_TCP_TYPE_DATA |
|
|
29
|
+
TUNNEL_TCP_TYPE_ERROR |
|
|
30
|
+
TUNNEL_TCP_TYPE_CLOSE |
|
|
31
|
+
TUNNEL_TCP_TYPE_PING |
|
|
32
|
+
TUNNEL_TCP_TYPE_PONG |
|
|
33
|
+
TUNNEL_TCP_TYPE_ACK
|
|
34
|
+
|
|
35
|
+
export type TCP_TUNNEL_DATA = {
|
|
36
|
+
type: TUNNEL_TCP_TYPE
|
|
37
|
+
srcId: number
|
|
38
|
+
dstId: number
|
|
39
|
+
srcChannel: number
|
|
40
|
+
dstChannel: number
|
|
41
|
+
buffer: Uint8Array<ArrayBuffer>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type TUNNEL_TCP_DATA_LISTEN = {
|
|
45
|
+
key: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type TUNNEL_TCP_DATA_CONNECT = {
|
|
49
|
+
key: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type TUNNEL_TCP_DATA_PINGPONG = {
|
|
53
|
+
time: number
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type TUNNEL_TCP_SERVER = {
|
|
57
|
+
id: number
|
|
58
|
+
encodeWriter: WritableStreamDefaultWriter<Uint8Array>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type TUNNEL_TCP_SERVER_HELPER = {
|
|
62
|
+
readable: ReadableStream<Uint8Array<ArrayBuffer>>
|
|
63
|
+
writable: WritableStream<Uint8Array<ArrayBuffer>>
|
|
64
|
+
reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>
|
|
65
|
+
writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
|
|
66
|
+
dstId: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type TUNNEL_TCP_CLIENT_HELPER = {
|
|
70
|
+
readable: ReadableStream<Uint8Array<ArrayBuffer>>
|
|
71
|
+
writable: WritableStream<Uint8Array<ArrayBuffer>>
|
|
72
|
+
reader: ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>
|
|
73
|
+
writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
|
|
74
|
+
param: TunnelTcpClientHelperParam
|
|
75
|
+
listen: (param: {
|
|
76
|
+
clientKey?: string
|
|
77
|
+
tunnelKey: string
|
|
78
|
+
host?: string
|
|
79
|
+
port: number
|
|
80
|
+
}) => Promise<void>
|
|
81
|
+
connect: (param: {
|
|
82
|
+
clientKey?: string
|
|
83
|
+
tunnelKey: string
|
|
84
|
+
port: number
|
|
85
|
+
}) => Promise<void>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
export type SocketChannel = {
|
|
90
|
+
writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
|
|
91
|
+
socket: net.Socket
|
|
92
|
+
srcId: number
|
|
93
|
+
dstId: number
|
|
94
|
+
srcChannel: number
|
|
95
|
+
dstChannel: number
|
|
96
|
+
notify: (size: number) => void
|
|
97
|
+
recvPackSize: number
|
|
98
|
+
key_iv: [CryptoKey, Uint8Array<ArrayBuffer>]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
export type TunnelTcpServerHelperParam = {
|
|
103
|
+
uniqueId: number
|
|
104
|
+
listenMap: Map<string, number>
|
|
105
|
+
dstMap: Map<number, TUNNEL_TCP_SERVER>
|
|
106
|
+
serverKey?: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type TunnelTcpClientHelperParam = {
|
|
110
|
+
signal: AbortSignal
|
|
111
|
+
serverKey?: string
|
|
112
|
+
uniqueId: number
|
|
113
|
+
clientDataId: number
|
|
114
|
+
}
|