js-tcp-tunnel 1.0.0 → 1.0.2

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