js-tcp-tunnel 1.0.3 → 1.1.1
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 +164 -100
- package/src/lib.test.js +141 -1
- package/src/types.ts +13 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-tcp-tunnel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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
|
@@ -6,9 +6,10 @@ import https from 'node:https'
|
|
|
6
6
|
import { ReadableStream, TransformStream, WritableStream } from 'node:stream/web'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
+
* @import { ReadableStreamDefaultReader, WritableStreamDefaultWriter } from 'node:stream/web'
|
|
9
10
|
* @import {WebSocketServer} from 'ws'
|
|
10
|
-
* @import Router from 'koa
|
|
11
|
-
* @import {SocketChannel, TCP_TUNNEL_DATA,
|
|
11
|
+
* @import Router from '@koa/router'
|
|
12
|
+
* @import {ConnectParam, ListenParam, SocketChannel, TCP_TUNNEL_DATA, TUNNEL_TCP_DATA_CONNECT, TUNNEL_TCP_DATA_LISTEN, TUNNEL_TCP_DATA_PINGPONG, TunnelTcpClientHelperParam, TunnelTcpServerHelperParam} from './types.js'
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
const DEBUG_TUNNEL_TCP = false
|
|
@@ -68,8 +69,8 @@ export function Promise_withResolvers() {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
/**
|
|
71
|
-
* @param {Promise<[CryptoKey,Uint8Array]>} key_iv
|
|
72
|
-
* @returns {TransformStream<Uint8Array
|
|
72
|
+
* @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
|
|
73
|
+
* @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
|
|
73
74
|
*/
|
|
74
75
|
export function createEncodeStream(key_iv) {
|
|
75
76
|
let key = null
|
|
@@ -86,8 +87,8 @@ export function createEncodeStream(key_iv) {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
/**
|
|
89
|
-
* @param {Promise<[CryptoKey,Uint8Array]>} key_iv
|
|
90
|
-
* @returns {TransformStream<Uint8Array
|
|
90
|
+
* @param {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>} key_iv
|
|
91
|
+
* @returns {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>}
|
|
91
92
|
*/
|
|
92
93
|
export function createDecodeStream(key_iv) {
|
|
93
94
|
let key = null
|
|
@@ -110,10 +111,10 @@ export function createDecodeStream(key_iv) {
|
|
|
110
111
|
const HEADER_CHECK = 0xb1f7705f
|
|
111
112
|
|
|
112
113
|
/**
|
|
113
|
-
* @param {Uint8Array[]} queue
|
|
114
|
+
* @param {Uint8Array<ArrayBuffer>[]} queue
|
|
114
115
|
* @param {CryptoKey} key
|
|
115
|
-
* @param {Uint8Array} iv
|
|
116
|
-
* @returns {Promise<Uint8Array
|
|
116
|
+
* @param {Uint8Array<ArrayBuffer>} iv
|
|
117
|
+
* @returns {Promise<Uint8Array<ArrayBuffer>>}
|
|
117
118
|
*/
|
|
118
119
|
export async function buildBufferData(queue, key, iv) {
|
|
119
120
|
let buffers = []
|
|
@@ -132,11 +133,11 @@ export async function buildBufferData(queue, key, iv) {
|
|
|
132
133
|
/**
|
|
133
134
|
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
134
135
|
* @param {CryptoKey} key
|
|
135
|
-
* @param {Uint8Array} iv
|
|
136
|
-
* @returns {Promise<[Uint8Array[],Uint8Array<ArrayBuffer>]>}
|
|
136
|
+
* @param {Uint8Array<ArrayBuffer>} iv
|
|
137
|
+
* @returns {Promise<[Uint8Array<ArrayBuffer>[],Uint8Array<ArrayBuffer>]>}
|
|
137
138
|
*/
|
|
138
139
|
export async function parseBufferData(buffer, key, iv) {
|
|
139
|
-
/** @type{Uint8Array[]} */
|
|
140
|
+
/** @type{Uint8Array<ArrayBuffer>[]} */
|
|
140
141
|
let queue = []
|
|
141
142
|
let offset = 0
|
|
142
143
|
let remain = new Uint8Array(0)
|
|
@@ -315,7 +316,7 @@ export function printTcpTunnelData(data) {
|
|
|
315
316
|
*
|
|
316
317
|
* @param {string} password
|
|
317
318
|
* @param {number} iterations
|
|
318
|
-
* @returns {Promise<[CryptoKey,Uint8Array]>}
|
|
319
|
+
* @returns {Promise<[CryptoKey,Uint8Array<ArrayBuffer>]>}
|
|
319
320
|
*/
|
|
320
321
|
export async function buildKeyIv(password, iterations) {
|
|
321
322
|
if (!TUNNEL_TCP_WITH_CRYPTO) return [null, null]
|
|
@@ -351,9 +352,9 @@ export async function buildKeyIv(password, iterations) {
|
|
|
351
352
|
|
|
352
353
|
/**
|
|
353
354
|
*
|
|
354
|
-
* @param {Uint8Array} data
|
|
355
|
+
* @param {Uint8Array<ArrayBuffer>} data
|
|
355
356
|
* @param {CryptoKey} key
|
|
356
|
-
* @param {Uint8Array} iv
|
|
357
|
+
* @param {Uint8Array<ArrayBuffer>} iv
|
|
357
358
|
* @returns
|
|
358
359
|
*/
|
|
359
360
|
export async function encrypt(data, key, iv) {
|
|
@@ -366,9 +367,9 @@ export async function encrypt(data, key, iv) {
|
|
|
366
367
|
}
|
|
367
368
|
|
|
368
369
|
/**
|
|
369
|
-
* @param {Uint8Array} data
|
|
370
|
+
* @param {Uint8Array<ArrayBuffer>} data
|
|
370
371
|
* @param {CryptoKey} key
|
|
371
|
-
* @param {Uint8Array} iv
|
|
372
|
+
* @param {Uint8Array<ArrayBuffer>} iv
|
|
372
373
|
* @returns
|
|
373
374
|
*/
|
|
374
375
|
export async function decrypt(data, key, iv) {
|
|
@@ -437,7 +438,7 @@ export function buildTcpTunnelData(box) {
|
|
|
437
438
|
}
|
|
438
439
|
|
|
439
440
|
/**
|
|
440
|
-
* @param {Uint8Array} buffer
|
|
441
|
+
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
441
442
|
*/
|
|
442
443
|
export function parseTcpTunnelData(buffer) {
|
|
443
444
|
let offset = 0
|
|
@@ -534,25 +535,43 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
|
|
|
534
535
|
let signal = Promise_withResolvers()
|
|
535
536
|
signal.resolve()
|
|
536
537
|
let sendPackSize = 0
|
|
537
|
-
let
|
|
538
|
+
let remoteRecvPackSize = 0
|
|
538
539
|
channel.notify = (size) => {
|
|
539
|
-
|
|
540
|
+
remoteRecvPackSize = size
|
|
540
541
|
signal.resolve()
|
|
541
542
|
}
|
|
542
543
|
let [clientKey, clientIv] = channel.key_iv
|
|
543
544
|
let bufferedTransform = createTimeBufferedTransformStream(50)
|
|
545
|
+
let backPressureTimer = null
|
|
544
546
|
Readable.toWeb(socket).pipeThrough(bufferedTransform).pipeTo(new WritableStream({
|
|
547
|
+
/**
|
|
548
|
+
* @param {Uint8Array<ArrayBuffer>} chunk
|
|
549
|
+
*/
|
|
545
550
|
async write(chunk) {
|
|
546
551
|
const buffer = await encrypt(chunk, clientKey, clientIv)
|
|
547
|
-
let bufferPackSize = sendPackSize -
|
|
552
|
+
let bufferPackSize = sendPackSize - remoteRecvPackSize
|
|
553
|
+
if (DEBUG_TUNNEL_TCP) {
|
|
554
|
+
console.warn('bufferPackSize:', bufferPackSize)
|
|
555
|
+
}
|
|
548
556
|
if (bufferPackSize > 10) {
|
|
549
557
|
signal.resolve()
|
|
550
558
|
signal = Promise_withResolvers()
|
|
559
|
+
const s = signal
|
|
560
|
+
backPressureTimer = setTimeout(() => {
|
|
561
|
+
s.resolve()
|
|
562
|
+
console.error('pipeSocketDataWithChannel timeout close channel')
|
|
563
|
+
sendPackSize = 0
|
|
564
|
+
this.close()
|
|
565
|
+
}, 10_000).unref()
|
|
551
566
|
if (DEBUG_TUNNEL_TCP) {
|
|
552
|
-
console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:',
|
|
567
|
+
console.info('stop wait signal', ' sendPackSize:', sendPackSize, ' recvPackSize:', remoteRecvPackSize, ' bufferPackSize:', bufferPackSize)
|
|
553
568
|
}
|
|
554
569
|
}
|
|
555
570
|
await signal.promise
|
|
571
|
+
if (backPressureTimer) {
|
|
572
|
+
clearTimeout(backPressureTimer)
|
|
573
|
+
backPressureTimer = null
|
|
574
|
+
}
|
|
556
575
|
await encodeWriter.write(buildTcpTunnelData({
|
|
557
576
|
type: TUNNEL_TCP_TYPE_DATA,
|
|
558
577
|
srcId: channel.srcId,
|
|
@@ -573,6 +592,7 @@ export function pipeSocketDataWithChannel(channelMap, channelId, encodeWriter) {
|
|
|
573
592
|
buffer: new Uint8Array(0),
|
|
574
593
|
})).catch((err) => { console.error('web stream write error', err.message) })
|
|
575
594
|
channelMap.delete(channelId)
|
|
595
|
+
socket.destroy()
|
|
576
596
|
}
|
|
577
597
|
})).catch((err) => {
|
|
578
598
|
console.error('web stream error', err.message)
|
|
@@ -611,8 +631,8 @@ function natTunnelData(param, data) {
|
|
|
611
631
|
|
|
612
632
|
/**
|
|
613
633
|
* @param {TunnelTcpServerHelperParam} param
|
|
614
|
-
* @param {WritableStreamDefaultWriter<Uint8Array<
|
|
615
|
-
* @param {Uint8Array<
|
|
634
|
+
* @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} encodeWriter
|
|
635
|
+
* @param {Uint8Array<ArrayBuffer>} chunk
|
|
616
636
|
*/
|
|
617
637
|
async function dispatchServerBufferData(param, encodeWriter, chunk) {
|
|
618
638
|
let data = parseTcpTunnelData(chunk)
|
|
@@ -658,10 +678,10 @@ async function dispatchServerBufferData(param, encodeWriter, chunk) {
|
|
|
658
678
|
/**
|
|
659
679
|
* @param {TunnelTcpClientHelperParam} param
|
|
660
680
|
* @param {{ (): Promise<void>; }} setup
|
|
661
|
-
* @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array]}>} listenKeyParamMap
|
|
681
|
+
* @param {Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} listenKeyParamMap
|
|
662
682
|
* @param {Map<number, SocketChannel>} channelMap
|
|
663
|
-
* @param {WritableStreamDefaultWriter<Uint8Array<
|
|
664
|
-
* @param {Uint8Array<
|
|
683
|
+
* @param {WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} encodeWriter
|
|
684
|
+
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
665
685
|
*/
|
|
666
686
|
async function dispatchClientBufferData(param, setup, listenKeyParamMap, channelMap, encodeWriter, buffer) {
|
|
667
687
|
let data = parseTcpTunnelData(buffer)
|
|
@@ -852,6 +872,9 @@ export function createTunnelTcpServerHelper(param) {
|
|
|
852
872
|
if (DEBUG_TUNNEL_TCP) {
|
|
853
873
|
let writer = encodeWriter
|
|
854
874
|
encodeWriter = new WritableStream({
|
|
875
|
+
/**
|
|
876
|
+
* @param {Uint8Array<ArrayBuffer>} chunk
|
|
877
|
+
*/
|
|
855
878
|
async write(chunk) {
|
|
856
879
|
tcpTunnelDataSend += chunk.length
|
|
857
880
|
let data = parseTcpTunnelData(chunk)
|
|
@@ -862,6 +885,9 @@ export function createTunnelTcpServerHelper(param) {
|
|
|
862
885
|
}
|
|
863
886
|
|
|
864
887
|
decode.readable.pipeTo(new WritableStream({
|
|
888
|
+
/**
|
|
889
|
+
* @param {Uint8Array<ArrayBuffer>} chunk
|
|
890
|
+
*/
|
|
865
891
|
async write(chunk) {
|
|
866
892
|
try {
|
|
867
893
|
await dispatchServerBufferData(param, encodeWriter, chunk)
|
|
@@ -882,63 +908,112 @@ export function createTunnelTcpServerHelper(param) {
|
|
|
882
908
|
buffer: new Uint8Array(0),
|
|
883
909
|
}))
|
|
884
910
|
|
|
885
|
-
|
|
886
|
-
let helper = { readable: encode.readable, writable: decode.writable, reader: null, writer: null, dstId: id, }
|
|
887
|
-
return helper
|
|
911
|
+
return new TunnelTcpServerHelper(id, encode, decode)
|
|
888
912
|
}
|
|
889
913
|
|
|
890
914
|
/**
|
|
891
915
|
* @param {TunnelTcpClientHelperParam} param
|
|
892
916
|
*/
|
|
893
917
|
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
918
|
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
919
|
let encode = createEncodeStream(server_key_iv)
|
|
909
920
|
let decode = createDecodeStream(server_key_iv)
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
+
return new TunnelTcpClientHelper(param, encode, decode)
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
export class TunnelTcpServerHelper {
|
|
925
|
+
dstId = 0
|
|
926
|
+
/** @type{ReadableStream<Uint8Array<ArrayBuffer>>} */
|
|
927
|
+
readable = null
|
|
928
|
+
/** @type{WritableStream<Uint8Array<ArrayBuffer>>} */
|
|
929
|
+
writable = null
|
|
930
|
+
/** @type{ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */
|
|
931
|
+
reader = null
|
|
932
|
+
/** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
|
|
933
|
+
writer = null
|
|
934
|
+
/**
|
|
935
|
+
* @param {number} id
|
|
936
|
+
* @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} encode
|
|
937
|
+
* @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} decode
|
|
938
|
+
*/
|
|
939
|
+
constructor(id, encode, decode) {
|
|
940
|
+
this.dstId = id
|
|
941
|
+
this.readable = encode.readable
|
|
942
|
+
this.writable = decode.writable
|
|
921
943
|
}
|
|
944
|
+
}
|
|
922
945
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
946
|
+
export class TunnelTcpClientHelper {
|
|
947
|
+
/** @type{ReadableStream<Uint8Array<ArrayBuffer>>} */
|
|
948
|
+
readable = null
|
|
949
|
+
/** @type{WritableStream<Uint8Array<ArrayBuffer>>} */
|
|
950
|
+
writable = null
|
|
951
|
+
/** @type{ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */
|
|
952
|
+
reader = null
|
|
953
|
+
/** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
|
|
954
|
+
writer = null
|
|
955
|
+
/** @type{TunnelTcpClientHelperParam} */
|
|
956
|
+
param = null
|
|
957
|
+
|
|
958
|
+
/** @type{Map<number,SocketChannel>} */
|
|
959
|
+
channelMap = new Map()
|
|
960
|
+
/** @type{Map<string,{host:string;port:number;key_iv:[CryptoKey, Uint8Array<ArrayBuffer>]}>} */
|
|
961
|
+
listenKeyParamMap = new Map()
|
|
962
|
+
/** @type{Set<ListenParam>} */
|
|
963
|
+
listenParams = new Set()
|
|
964
|
+
/** @type{WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>} */
|
|
965
|
+
encodeWriter = null
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* @param {TunnelTcpClientHelperParam} param
|
|
969
|
+
* @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} encode
|
|
970
|
+
* @param {TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>} decode
|
|
971
|
+
*/
|
|
972
|
+
constructor(param, encode, decode) {
|
|
973
|
+
this.param = param
|
|
974
|
+
this.readable = encode.readable
|
|
975
|
+
this.writable = decode.writable
|
|
976
|
+
|
|
977
|
+
param.signal.addEventListener('abort', () => {
|
|
978
|
+
this.channelMap.values().forEach(o => {
|
|
979
|
+
o.socket.destroy()
|
|
980
|
+
})
|
|
981
|
+
})
|
|
982
|
+
|
|
983
|
+
this.encodeWriter = encode.writable.getWriter()
|
|
984
|
+
if (DEBUG_TUNNEL_TCP) {
|
|
985
|
+
let writer = this.encodeWriter
|
|
986
|
+
this.encodeWriter = new WritableStream({
|
|
987
|
+
async write(chunk) {
|
|
988
|
+
tcpTunnelDataSend += chunk.length
|
|
989
|
+
let data = parseTcpTunnelData(chunk)
|
|
990
|
+
console.info('send', printTcpTunnelData(data))
|
|
991
|
+
writer.write(chunk)
|
|
992
|
+
}
|
|
993
|
+
}).getWriter()
|
|
930
994
|
}
|
|
931
|
-
}))
|
|
932
995
|
|
|
933
|
-
|
|
934
|
-
|
|
996
|
+
const thiz = this
|
|
997
|
+
decode.readable.pipeTo(new WritableStream({
|
|
998
|
+
/**
|
|
999
|
+
* @param {Uint8Array<ArrayBuffer>} buffer
|
|
1000
|
+
*/
|
|
1001
|
+
async write(buffer) {
|
|
1002
|
+
try {
|
|
1003
|
+
await dispatchClientBufferData(param, () => thiz.setup(), thiz.listenKeyParamMap, thiz.channelMap, thiz.encodeWriter, buffer)
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
console.error('decode.readable.pipeTo.write', error.message)
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}))
|
|
1009
|
+
}
|
|
935
1010
|
|
|
936
|
-
async
|
|
937
|
-
channelMap.forEach((channel) => {
|
|
938
|
-
channel.srcId = param.clientDataId
|
|
1011
|
+
async setup() {
|
|
1012
|
+
this.channelMap.forEach((channel) => {
|
|
1013
|
+
channel.srcId = this.param.clientDataId
|
|
939
1014
|
/** @type{TUNNEL_TCP_DATA_PINGPONG} */
|
|
940
1015
|
let pingData = { time: Date.now() }
|
|
941
|
-
encodeWriter.write(buildTcpTunnelData({
|
|
1016
|
+
this.encodeWriter.write(buildTcpTunnelData({
|
|
942
1017
|
type: TUNNEL_TCP_TYPE_PING,
|
|
943
1018
|
srcId: channel.srcId,
|
|
944
1019
|
srcChannel: channel.srcChannel,
|
|
@@ -947,34 +1022,29 @@ export function createTunnelTcpClientHelper(param) {
|
|
|
947
1022
|
buffer: Uint8Array_from(JSON.stringify(pingData)),
|
|
948
1023
|
}))
|
|
949
1024
|
})
|
|
950
|
-
for (const param of listenParams) {
|
|
951
|
-
await listen(param)
|
|
1025
|
+
for (const param of this.listenParams) {
|
|
1026
|
+
await this.listen(param)
|
|
952
1027
|
}
|
|
953
1028
|
}
|
|
954
1029
|
|
|
955
1030
|
/**
|
|
956
|
-
* @param {
|
|
957
|
-
* clientKey?:string;
|
|
958
|
-
* tunnelKey:string;
|
|
959
|
-
* host?:string;
|
|
960
|
-
* port:number;
|
|
961
|
-
* }} param
|
|
1031
|
+
* @param {ListenParam} param
|
|
962
1032
|
*/
|
|
963
|
-
async
|
|
964
|
-
listenParams.add(param)
|
|
965
|
-
console.info('listenParams size', listenParams.size)
|
|
966
|
-
if (
|
|
1033
|
+
async listen(param) {
|
|
1034
|
+
this.listenParams.add(param)
|
|
1035
|
+
console.info('listenParams size', this.listenParams.size)
|
|
1036
|
+
if (this.param.clientDataId < 1) {
|
|
967
1037
|
console.info('skip send listen dataId == 0')
|
|
968
1038
|
return
|
|
969
1039
|
}
|
|
970
1040
|
let key = sha512(param.tunnelKey)
|
|
971
1041
|
let key_iv = await buildKeyIv(param.clientKey, 10)
|
|
972
|
-
listenKeyParamMap.set(key, { host: param.host, port: param.port, key_iv })
|
|
1042
|
+
this.listenKeyParamMap.set(key, { host: param.host, port: param.port, key_iv })
|
|
973
1043
|
/** @type{TUNNEL_TCP_DATA_LISTEN} */
|
|
974
1044
|
let listenData = { key: key }
|
|
975
|
-
await encodeWriter.write(buildTcpTunnelData({
|
|
1045
|
+
await this.encodeWriter.write(buildTcpTunnelData({
|
|
976
1046
|
type: TUNNEL_TCP_TYPE_LISTEN,
|
|
977
|
-
srcId:
|
|
1047
|
+
srcId: this.param.clientDataId,
|
|
978
1048
|
srcChannel: 0,
|
|
979
1049
|
dstId: 0,
|
|
980
1050
|
dstChannel: 0,
|
|
@@ -983,19 +1053,15 @@ export function createTunnelTcpClientHelper(param) {
|
|
|
983
1053
|
}
|
|
984
1054
|
|
|
985
1055
|
/**
|
|
986
|
-
* @param {
|
|
987
|
-
* clientKey?:string;
|
|
988
|
-
* tunnelKey: string;
|
|
989
|
-
* port: number;
|
|
990
|
-
* }} param
|
|
1056
|
+
* @param {ConnectParam} param
|
|
991
1057
|
*/
|
|
992
|
-
async
|
|
1058
|
+
async connect(param) {
|
|
993
1059
|
let key_iv = await buildKeyIv(param.clientKey, 10)
|
|
994
1060
|
let server = net.createServer((socket) => {
|
|
995
|
-
let channelId =
|
|
1061
|
+
let channelId = this.param.uniqueId++
|
|
996
1062
|
socket.on('error', (err) => {
|
|
997
1063
|
console.error('createTunnelTcpClientHelper on socket error', err.message)
|
|
998
|
-
channelMap.delete(channelId)
|
|
1064
|
+
this.channelMap.delete(channelId)
|
|
999
1065
|
})
|
|
1000
1066
|
/** @type{TUNNEL_TCP_DATA_CONNECT} */
|
|
1001
1067
|
let connectData = { key: sha512(param.tunnelKey) }
|
|
@@ -1003,7 +1069,7 @@ export function createTunnelTcpClientHelper(param) {
|
|
|
1003
1069
|
let channel = {
|
|
1004
1070
|
writer: Writable.toWeb(socket).getWriter(),
|
|
1005
1071
|
socket,
|
|
1006
|
-
srcId:
|
|
1072
|
+
srcId: this.param.clientDataId,
|
|
1007
1073
|
srcChannel: channelId,
|
|
1008
1074
|
dstId: 0,
|
|
1009
1075
|
dstChannel: 0,
|
|
@@ -1011,8 +1077,8 @@ export function createTunnelTcpClientHelper(param) {
|
|
|
1011
1077
|
key_iv,
|
|
1012
1078
|
notify: null,
|
|
1013
1079
|
}
|
|
1014
|
-
channelMap.set(channelId, channel)
|
|
1015
|
-
encodeWriter.write(buildTcpTunnelData({
|
|
1080
|
+
this.channelMap.set(channelId, channel)
|
|
1081
|
+
this.encodeWriter.write(buildTcpTunnelData({
|
|
1016
1082
|
type: TUNNEL_TCP_TYPE_CONNECT,
|
|
1017
1083
|
srcId: channel.srcId,
|
|
1018
1084
|
srcChannel: channel.srcChannel,
|
|
@@ -1024,13 +1090,8 @@ export function createTunnelTcpClientHelper(param) {
|
|
|
1024
1090
|
server.on('error', (err) => {
|
|
1025
1091
|
console.error('createTunnelTcpClientHelper connect on server error', err.message)
|
|
1026
1092
|
})
|
|
1027
|
-
|
|
1093
|
+
this.param.signal.addEventListener('abort', () => { server.close() })
|
|
1028
1094
|
}
|
|
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
1095
|
}
|
|
1035
1096
|
|
|
1036
1097
|
|
|
@@ -1351,6 +1412,9 @@ export function createTunnelTcpClientHttp(param) {
|
|
|
1351
1412
|
let socketWriter = null
|
|
1352
1413
|
let bufferedTransform = createTimeBufferedTransformStream(50)
|
|
1353
1414
|
helper.readable.pipeThrough(bufferedTransform).pipeTo(new WritableStream({
|
|
1415
|
+
/**
|
|
1416
|
+
* @param {Uint8Array<ArrayBufferLike>} chunk
|
|
1417
|
+
*/
|
|
1354
1418
|
async write(chunk) {
|
|
1355
1419
|
while (!param.signal.aborted && socketWriter == null) {
|
|
1356
1420
|
await signal.promise
|
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
|
|
|
@@ -509,4 +509,144 @@ test('test-connect', { skip: SKIP_MANAUAL_TEST }, async () => {
|
|
|
509
509
|
|
|
510
510
|
await sleep(10000_000)
|
|
511
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!')
|
|
512
652
|
})
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import net from 'node:net'
|
|
2
|
-
import { WritableStream } from "stream/web"
|
|
2
|
+
import { WritableStream, ReadableStream, ReadableStreamDefaultReader, WritableStreamDefaultWriter } from "stream/web"
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
export type PromiseResolvers = {
|
|
@@ -38,7 +38,7 @@ export type TCP_TUNNEL_DATA = {
|
|
|
38
38
|
dstId: number
|
|
39
39
|
srcChannel: number
|
|
40
40
|
dstChannel: number
|
|
41
|
-
buffer: Uint8Array
|
|
41
|
+
buffer: Uint8Array<ArrayBuffer>
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export type TUNNEL_TCP_DATA_LISTEN = {
|
|
@@ -58,36 +58,21 @@ export type TUNNEL_TCP_SERVER = {
|
|
|
58
58
|
encodeWriter: WritableStreamDefaultWriter<Uint8Array>
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export type
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
dstId: number
|
|
61
|
+
export type ListenParam = {
|
|
62
|
+
clientKey?: string;
|
|
63
|
+
tunnelKey: string;
|
|
64
|
+
host?: string;
|
|
65
|
+
port: number;
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
export type
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
writer: WritableStreamDefaultWriter<Uint8Array>
|
|
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>
|
|
68
|
+
export type ConnectParam = {
|
|
69
|
+
clientKey?: string;
|
|
70
|
+
tunnelKey: string;
|
|
71
|
+
port: number;
|
|
86
72
|
}
|
|
87
73
|
|
|
88
|
-
|
|
89
74
|
export type SocketChannel = {
|
|
90
|
-
writer: WritableStreamDefaultWriter<Uint8Array
|
|
75
|
+
writer: WritableStreamDefaultWriter<Uint8Array<ArrayBuffer>>
|
|
91
76
|
socket: net.Socket
|
|
92
77
|
srcId: number
|
|
93
78
|
dstId: number
|
|
@@ -95,7 +80,7 @@ export type SocketChannel = {
|
|
|
95
80
|
dstChannel: number
|
|
96
81
|
notify: (size: number) => void
|
|
97
82
|
recvPackSize: number
|
|
98
|
-
key_iv: [CryptoKey, Uint8Array]
|
|
83
|
+
key_iv: [CryptoKey, Uint8Array<ArrayBuffer>]
|
|
99
84
|
}
|
|
100
85
|
|
|
101
86
|
|