js-tcp-tunnel 1.0.1 → 1.0.3

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