bdy 1.10.14 → 1.11.0-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/distTs/bin/cli.js +1 -1
  2. package/distTs/package.json +1 -1
  3. package/distTs/src/agent/agent.js +302 -0
  4. package/distTs/src/agent/manager.js +41 -41
  5. package/distTs/src/agent/socket/client.js +159 -0
  6. package/distTs/src/agent/socket/tunnel.js +3 -3
  7. package/distTs/src/agent/wait.js +2 -2
  8. package/distTs/src/command/agent/disable.js +1 -1
  9. package/distTs/src/command/agent/enable.js +1 -1
  10. package/distTs/src/command/agent/install.js +45 -45
  11. package/distTs/src/command/agent/restart.js +8 -8
  12. package/distTs/src/command/agent/start.js +8 -8
  13. package/distTs/src/command/agent/status.js +15 -15
  14. package/distTs/src/command/agent/stop.js +8 -8
  15. package/distTs/src/command/agent/target/disable.js +1 -1
  16. package/distTs/src/command/agent/target/enable.js +1 -1
  17. package/distTs/src/command/agent/target/status.js +1 -1
  18. package/distTs/src/command/agent/tunnel/http.js +18 -18
  19. package/distTs/src/command/agent/tunnel/list.js +7 -7
  20. package/distTs/src/command/agent/tunnel/remove.js +9 -9
  21. package/distTs/src/command/agent/tunnel/start.js +14 -14
  22. package/distTs/src/command/agent/tunnel/status.js +11 -11
  23. package/distTs/src/command/agent/tunnel/tcp.js +19 -19
  24. package/distTs/src/command/agent/tunnel/tls.js +17 -17
  25. package/distTs/src/command/agent/tunnel.js +16 -16
  26. package/distTs/src/command/agent/uninstall.js +13 -13
  27. package/distTs/src/command/agent/update.js +12 -12
  28. package/distTs/src/command/agent/version.js +6 -6
  29. package/distTs/src/command/agent.js +18 -18
  30. package/distTs/src/command/config/add/http.js +15 -15
  31. package/distTs/src/command/config/add/tcp.js +15 -15
  32. package/distTs/src/command/config/add/tls.js +15 -15
  33. package/distTs/src/command/config/add.js +8 -8
  34. package/distTs/src/command/config/get/region.js +5 -5
  35. package/distTs/src/command/config/get/timeout.js +5 -5
  36. package/distTs/src/command/config/get/token.js +5 -5
  37. package/distTs/src/command/config/get/tunnel.js +9 -9
  38. package/distTs/src/command/config/get/tunnels.js +6 -6
  39. package/distTs/src/command/config/get/whitelist.js +5 -5
  40. package/distTs/src/command/config/get.js +14 -14
  41. package/distTs/src/command/config/remove/tunnel.js +9 -9
  42. package/distTs/src/command/config/remove.js +4 -4
  43. package/distTs/src/command/config/set/region.js +8 -8
  44. package/distTs/src/command/config/set/timeout.js +9 -9
  45. package/distTs/src/command/config/set/token.js +7 -7
  46. package/distTs/src/command/config/set/whitelist.js +8 -8
  47. package/distTs/src/command/config/set.js +10 -10
  48. package/distTs/src/command/pre.js +14 -14
  49. package/distTs/src/command/tunnel/config.js +17 -0
  50. package/distTs/src/command/tunnel/http.js +30 -0
  51. package/distTs/src/command/tunnel/start.js +28 -0
  52. package/distTs/src/command/tunnel/tcp.js +30 -0
  53. package/distTs/src/command/tunnel/tls.js +30 -0
  54. package/distTs/src/command/tunnel.js +19 -0
  55. package/distTs/src/command/ut/upload.js +6 -6
  56. package/distTs/src/command/ut.js +3 -2
  57. package/distTs/src/command/version.js +4 -4
  58. package/distTs/src/command/vt/close.js +4 -4
  59. package/distTs/src/command/vt/compare.js +16 -16
  60. package/distTs/src/command/vt/exec.js +12 -12
  61. package/distTs/src/command/vt/installBrowser.js +2 -2
  62. package/distTs/src/command/vt/scrap.js +17 -17
  63. package/distTs/src/command/vt/storybook.js +8 -8
  64. package/distTs/src/command/vt.js +5 -4
  65. package/distTs/src/index.js +12 -20
  66. package/distTs/src/input.js +38 -38
  67. package/distTs/src/logger.js +2 -2
  68. package/distTs/src/output.js +14 -14
  69. package/distTs/src/texts.js +5 -4
  70. package/distTs/src/tunnel/agent.js +3 -3
  71. package/distTs/src/tunnel/api/agent.js +99 -0
  72. package/distTs/src/tunnel/api/buddy.js +139 -0
  73. package/distTs/src/tunnel/cfg.js +234 -0
  74. package/distTs/src/tunnel/http/log.js +3 -3
  75. package/distTs/src/tunnel/http.js +29 -29
  76. package/distTs/src/tunnel/identification.js +13 -13
  77. package/distTs/src/tunnel/latency.js +3 -3
  78. package/distTs/src/tunnel/output/interactive/tunnel.js +860 -0
  79. package/distTs/src/tunnel/output/noninteractive/agent/tunnels.js +43 -0
  80. package/distTs/src/tunnel/output/noninteractive/config/tunnel.js +65 -0
  81. package/distTs/src/tunnel/output/noninteractive/config/tunnels.js +18 -0
  82. package/distTs/src/tunnel/output/noninteractive/tunnel.js +59 -0
  83. package/distTs/src/tunnel/server/cert.js +52 -0
  84. package/distTs/src/tunnel/server/http1.js +75 -0
  85. package/distTs/src/tunnel/server/http2.js +78 -0
  86. package/distTs/src/tunnel/server/sftp.js +497 -0
  87. package/distTs/src/tunnel/server/ssh.js +446 -0
  88. package/distTs/src/tunnel/server/tls.js +41 -0
  89. package/distTs/src/tunnel/ssh/client.js +197 -0
  90. package/distTs/src/tunnel/tcp.js +3 -3
  91. package/distTs/src/tunnel/tunnel.js +656 -0
  92. package/distTs/src/utils.js +2 -2
  93. package/distTs/src/visualTest/ci.js +10 -10
  94. package/distTs/src/visualTest/context.js +4 -4
  95. package/distTs/src/visualTest/requests.js +71 -71
  96. package/distTs/src/visualTest/resources.js +5 -5
  97. package/distTs/src/visualTest/server.js +9 -9
  98. package/distTs/src/visualTest/snapshots.js +18 -18
  99. package/distTs/src/visualTest/validation.js +3 -3
  100. package/package.json +1 -1
@@ -0,0 +1,446 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const events_1 = __importDefault(require("events"));
7
+ const ssh2_1 = __importDefault(require("ssh2"));
8
+ const logger_1 = __importDefault(require("../../logger"));
9
+ const crypto_1 = require("crypto");
10
+ const child_process_1 = require("child_process");
11
+ const node_net_1 = __importDefault(require("node:net"));
12
+ const sftp_1 = __importDefault(require("./sftp"));
13
+ const buddy_1 = __importDefault(require("../api/buddy"));
14
+ const pipeStreamToChannel = (stream, channel) => {
15
+ channel.once('exit', (code, signal) => {
16
+ logger_1.default.debug(`shell channel exit: ${code}, signal: ${signal}`);
17
+ if (stream.exit)
18
+ stream.exit(signal || code);
19
+ });
20
+ channel.on('close', () => {
21
+ stream.end();
22
+ });
23
+ channel.pipe(stream, { end: false }).pipe(channel);
24
+ channel.stderr.pipe(stream.stderr);
25
+ };
26
+ class ServerSsh extends events_1.default {
27
+ constructor(agent, login, password, hostKey) {
28
+ super();
29
+ this.agent = agent;
30
+ this.login = login;
31
+ this.password = password;
32
+ this.server = new ssh2_1.default.Server({
33
+ keepAlive: true,
34
+ hostKeys: [hostKey],
35
+ highWaterMark: 16 * 1024 * 1024,
36
+ ident: 'ssh2 server',
37
+ }, (client) => this.processClient(client));
38
+ this.server.listen();
39
+ }
40
+ stop() {
41
+ this.server.close();
42
+ this.server.removeAllListeners();
43
+ this.server = null;
44
+ }
45
+ handleSshTunnel(stream) {
46
+ this.server.injectSocket(stream);
47
+ }
48
+ checkValueSafe(input, allowed) {
49
+ const autoReject = input.length !== allowed.length;
50
+ if (autoReject)
51
+ allowed = input;
52
+ const isMatch = (0, crypto_1.timingSafeEqual)(input, allowed);
53
+ return (!autoReject && isMatch);
54
+ }
55
+ async verifyKey(ctx, keys) {
56
+ try {
57
+ for (let i = 0; i < keys.length; i += 1) {
58
+ const publicKey = ssh2_1.default.utils.parseKey(keys[i]);
59
+ if (ctx.key.algo !== publicKey.type)
60
+ continue;
61
+ if (!this.checkValueSafe(ctx.key.data, publicKey.getPublicSSH()))
62
+ continue;
63
+ if (ctx.signature && !publicKey.verify(ctx.blob, ctx.signature, ctx.hashAlgo))
64
+ continue;
65
+ return true;
66
+ }
67
+ }
68
+ catch {
69
+ // do nothing
70
+ }
71
+ return false;
72
+ }
73
+ createProxyConnection(privateKey) {
74
+ return new Promise((resolve, reject) => {
75
+ const client = new ssh2_1.default.Client();
76
+ client.once('error', (err) => {
77
+ client.removeAllListeners();
78
+ reject(err);
79
+ });
80
+ client.once('ready', () => {
81
+ client.removeAllListeners();
82
+ resolve(client);
83
+ });
84
+ client.connect({
85
+ host: '127.0.0.1',
86
+ port: 22,
87
+ username: 'root',
88
+ privateKey: privateKey
89
+ });
90
+ });
91
+ }
92
+ async authenticateClient(ctx) {
93
+ const allowed = ['publickey', 'password'];
94
+ logger_1.default.debug(`auth method: ${ctx.method}`);
95
+ if (!allowed.includes(ctx.method)) {
96
+ ctx.reject(allowed);
97
+ return null;
98
+ }
99
+ if (ctx.method === 'password') {
100
+ if (!this.checkValueSafe(Buffer.from(ctx.username), Buffer.from(this.login))) {
101
+ ctx.reject(allowed);
102
+ return null;
103
+ }
104
+ if (!this.checkValueSafe(Buffer.from(ctx.password), Buffer.from(this.password))) {
105
+ ctx.reject(allowed);
106
+ return null;
107
+ }
108
+ ctx.accept();
109
+ return;
110
+ }
111
+ if (ctx.method === 'publickey') {
112
+ let keys;
113
+ let privateKey;
114
+ try {
115
+ const resp = await buddy_1.default.fetchAgentKeys(this.agent.id, this.agent.host, this.agent.token);
116
+ logger_1.default.debug(resp);
117
+ keys = resp.keys;
118
+ privateKey = resp.privateKey;
119
+ }
120
+ catch {
121
+ ctx.reject(allowed);
122
+ return null;
123
+ }
124
+ const verified = await this.verifyKey(ctx, keys);
125
+ if (!verified) {
126
+ logger_1.default.debug('not verified');
127
+ ctx.reject(allowed);
128
+ return null;
129
+ }
130
+ logger_1.default.debug('verified');
131
+ try {
132
+ const proxyClient = await this.createProxyConnection(privateKey);
133
+ logger_1.default.debug('proxy connected');
134
+ ctx.accept();
135
+ return proxyClient;
136
+ }
137
+ catch (err) {
138
+ logger_1.default.debug('proxy not connected');
139
+ logger_1.default.debug(err);
140
+ ctx.reject(allowed);
141
+ return null;
142
+ }
143
+ }
144
+ ctx.reject(allowed);
145
+ return null;
146
+ }
147
+ localExec(stream, command, env) {
148
+ const s = process.hrtime();
149
+ (0, child_process_1.exec)(command, {
150
+ env,
151
+ }, (err, stdout, stderr) => {
152
+ if (stream) {
153
+ stream.stderr.write(stderr);
154
+ stream.write(stdout);
155
+ stream.exit(!err ? 0 : 1);
156
+ stream.end();
157
+ stream = null;
158
+ }
159
+ const [seconds, nano] = process.hrtime(s);
160
+ const ms = seconds * 1000 + nano / 1000 / 1000;
161
+ logger_1.default.debug(`local ssh exec ends: ${ms}ms`);
162
+ });
163
+ }
164
+ clientSession(session, client, proxyClient) {
165
+ let env = {};
166
+ let sftp;
167
+ let channel;
168
+ let pty;
169
+ let x11;
170
+ const closeSession = () => {
171
+ logger_1.default.debug('ssh close session');
172
+ env = null;
173
+ pty = null;
174
+ x11 = null;
175
+ session.removeAllListeners();
176
+ session = null;
177
+ if (sftp) {
178
+ sftp.destroy();
179
+ sftp = null;
180
+ }
181
+ if (channel) {
182
+ channel.removeAllListeners();
183
+ channel = null;
184
+ }
185
+ client.removeListener('close', closeSession);
186
+ client = null;
187
+ };
188
+ client.on('close', closeSession);
189
+ session.on('close', closeSession);
190
+ session.on('end', closeSession);
191
+ session.on('pty', (accept, reject, info) => {
192
+ logger_1.default.debug('ssh pty');
193
+ logger_1.default.debug(info);
194
+ if (!proxyClient) {
195
+ if (reject)
196
+ reject();
197
+ return;
198
+ }
199
+ pty = info;
200
+ if (accept)
201
+ accept();
202
+ });
203
+ session.on('window-change', (accept, reject, info) => {
204
+ logger_1.default.debug('ssh window-change');
205
+ logger_1.default.debug(info);
206
+ if (!proxyClient) {
207
+ if (reject)
208
+ reject();
209
+ return;
210
+ }
211
+ if (!pty)
212
+ pty = {};
213
+ pty.cols = info.cols;
214
+ pty.height = info.height;
215
+ pty.rows = info.rows;
216
+ pty.width = info.width;
217
+ if (channel && channel.setWindow)
218
+ channel.setWindow(pty.rows, pty.cols, pty.height, pty.width);
219
+ if (accept)
220
+ accept();
221
+ });
222
+ session.on('shell', (accept, reject) => {
223
+ logger_1.default.debug('ssh shell');
224
+ if (!proxyClient || !accept) {
225
+ if (reject)
226
+ reject();
227
+ return;
228
+ }
229
+ const stream = accept();
230
+ proxyClient.shell(pty, {
231
+ env,
232
+ x11
233
+ }, (err, c) => {
234
+ if (err || !c) {
235
+ if (reject)
236
+ reject();
237
+ return;
238
+ }
239
+ pipeStreamToChannel(stream, c);
240
+ });
241
+ });
242
+ session.on('signal', (accept, reject, info) => {
243
+ logger_1.default.debug('ssh signal');
244
+ logger_1.default.debug(info);
245
+ const { name } = info;
246
+ if (!proxyClient || !name || !channel || !channel.signal) {
247
+ if (reject)
248
+ reject();
249
+ return;
250
+ }
251
+ channel.signal(name);
252
+ if (accept)
253
+ accept();
254
+ });
255
+ session.on('subsystem', (accept, reject, info) => {
256
+ logger_1.default.debug('ssh subsystem');
257
+ logger_1.default.debug(info);
258
+ const { name } = info;
259
+ if (!proxyClient || !accept || !name) {
260
+ if (reject)
261
+ reject();
262
+ return;
263
+ }
264
+ const stream = accept();
265
+ proxyClient.subsys(name, (err, c) => {
266
+ if (err || !c) {
267
+ if (reject)
268
+ reject();
269
+ return;
270
+ }
271
+ pipeStreamToChannel(stream, c);
272
+ });
273
+ });
274
+ session.on('x11', (accept, reject, info) => {
275
+ logger_1.default.debug('ssh x11');
276
+ logger_1.default.debug(info);
277
+ if (!proxyClient) {
278
+ if (reject)
279
+ reject();
280
+ return;
281
+ }
282
+ x11 = info;
283
+ if (accept)
284
+ accept();
285
+ });
286
+ session.on('exec', (accept, reject, info) => {
287
+ logger_1.default.debug('ssh exec');
288
+ logger_1.default.debug(info);
289
+ if (!info.command || !accept) {
290
+ if (reject)
291
+ reject();
292
+ return;
293
+ }
294
+ const stream = accept();
295
+ if (!proxyClient) {
296
+ this.localExec(stream, info.command, env);
297
+ return;
298
+ }
299
+ proxyClient.exec(info.command, {
300
+ env,
301
+ pty,
302
+ x11
303
+ }, (err, c) => {
304
+ if (err || !c) {
305
+ if (reject)
306
+ reject();
307
+ return;
308
+ }
309
+ pipeStreamToChannel(stream, c);
310
+ });
311
+ });
312
+ session.on('env', (accept, reject, info) => {
313
+ logger_1.default.debug('ssh env');
314
+ logger_1.default.debug(info);
315
+ Object.keys(info || {}).forEach((key) => {
316
+ env[key] = info[key];
317
+ });
318
+ if (accept)
319
+ accept();
320
+ });
321
+ session.on('sftp', (accept, reject) => {
322
+ logger_1.default.debug('sftp');
323
+ if (!sftp && accept) {
324
+ sftp = new sftp_1.default(accept());
325
+ }
326
+ else if (reject) {
327
+ reject();
328
+ }
329
+ });
330
+ }
331
+ processClient(client) {
332
+ logger_1.default.debug('new ssh client');
333
+ let proxyClient;
334
+ client.setNoDelay();
335
+ client.on('authentication', async (ctx) => {
336
+ logger_1.default.debug('ssh authentication');
337
+ proxyClient = await this.authenticateClient(ctx);
338
+ if (proxyClient) {
339
+ proxyClient.on('tcp connection', (details, accept, reject) => {
340
+ logger_1.default.debug('new ssh client forward connection');
341
+ logger_1.default.debug(details);
342
+ const { destIP, destPort, srcIP, srcPort } = details;
343
+ client.forwardOut(destIP, destPort, srcIP, srcPort, (err, channel) => {
344
+ logger_1.default.debug('new ssh client forward out connection');
345
+ logger_1.default.debug({ destIP, destPort, srcIP, srcPort });
346
+ if (err) {
347
+ reject();
348
+ return;
349
+ }
350
+ const stream = accept();
351
+ channel.pipe(stream).pipe(channel);
352
+ });
353
+ });
354
+ }
355
+ });
356
+ client.on('close', () => {
357
+ logger_1.default.debug('ssh close');
358
+ if (proxyClient) {
359
+ try {
360
+ proxyClient.removeAllListeners();
361
+ proxyClient.end();
362
+ }
363
+ catch {
364
+ // do nothing
365
+ }
366
+ proxyClient = null;
367
+ }
368
+ setTimeout(() => {
369
+ client.removeAllListeners();
370
+ client = null;
371
+ }, 1000);
372
+ });
373
+ client.on('tcpip', (accept, reject, info) => {
374
+ logger_1.default.debug('ssh tcpip request');
375
+ logger_1.default.debug(info);
376
+ const { destIP, destPort } = info;
377
+ let accepted = false;
378
+ const socket = node_net_1.default.connect({
379
+ host: destIP,
380
+ port: destPort
381
+ });
382
+ socket.on('connect', () => {
383
+ accepted = true;
384
+ const channel = accept();
385
+ channel.pipe(socket).pipe(channel);
386
+ });
387
+ const closeSocket = () => {
388
+ try {
389
+ socket.removeAllListeners();
390
+ socket.end();
391
+ }
392
+ catch {
393
+ // do nothing
394
+ }
395
+ if (!accepted)
396
+ reject();
397
+ };
398
+ socket.setTimeout(10000);
399
+ socket.on('timeout', closeSocket);
400
+ socket.on('error', closeSocket);
401
+ socket.on('close', closeSocket);
402
+ });
403
+ client.on('request', (accept, reject, name, info) => {
404
+ logger_1.default.debug('ssh forward request');
405
+ logger_1.default.debug(name);
406
+ logger_1.default.debug(info);
407
+ if (!proxyClient) {
408
+ reject();
409
+ return;
410
+ }
411
+ if (name === 'tcpip-forward') {
412
+ let { bindAddr, bindPort } = info;
413
+ proxyClient.forwardIn(bindAddr, bindPort, (err, port) => {
414
+ if (err) {
415
+ reject();
416
+ return;
417
+ }
418
+ accept(port);
419
+ });
420
+ return;
421
+ }
422
+ if (name === 'cancel-tcpip-forward') {
423
+ const { bindAddr, bindPort } = info;
424
+ proxyClient.unforwardIn(bindAddr, bindPort, (err) => {
425
+ if (err) {
426
+ reject();
427
+ return;
428
+ }
429
+ accept();
430
+ });
431
+ return;
432
+ }
433
+ reject();
434
+ });
435
+ client.on('error', (err) => {
436
+ logger_1.default.debug('Error on ssh server client');
437
+ logger_1.default.debug(err);
438
+ client.end();
439
+ });
440
+ client.on('session', (accept) => {
441
+ logger_1.default.debug('ssh session');
442
+ this.clientSession(accept(), client, proxyClient);
443
+ });
444
+ }
445
+ }
446
+ exports.default = ServerSsh;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const events_1 = __importDefault(require("events"));
7
+ const tls_1 = __importDefault(require("tls"));
8
+ const cert_js_1 = __importDefault(require("./cert.js"));
9
+ const utils_1 = require("../../utils");
10
+ class ServerTls extends events_1.default {
11
+ constructor(key, cert, ca) {
12
+ super();
13
+ this.key = key;
14
+ this.cert = cert;
15
+ this.ca = ca;
16
+ if (!this.key || !this.cert) {
17
+ const c = new cert_js_1.default();
18
+ this.key = c.key;
19
+ this.cert = c.cert;
20
+ }
21
+ this.server = tls_1.default.createServer({
22
+ handshakeTimeout: 60000,
23
+ key: this.key,
24
+ cert: this.cert,
25
+ ca: this.ca,
26
+ rejectUnauthorized: !!this.ca,
27
+ requestCert: !!this.ca,
28
+ }, (socket) => this.processSocket(socket));
29
+ }
30
+ async processSocket(socket) {
31
+ this.emit(utils_1.TLS_SOCKET, socket);
32
+ }
33
+ handleSshTunnel(channel) {
34
+ this.server.emit('connection', channel);
35
+ }
36
+ stop() {
37
+ this.server.removeAllListeners();
38
+ this.server = null;
39
+ }
40
+ }
41
+ exports.default = ServerTls;
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const ssh2_1 = require("ssh2");
7
+ const events_1 = __importDefault(require("events"));
8
+ const logger_1 = __importDefault(require("../../logger"));
9
+ const texts_1 = require("../../texts");
10
+ const utils_1 = require("../../utils");
11
+ class SshClient extends events_1.default {
12
+ constructor(ip, port, username, password) {
13
+ super();
14
+ this.ip = ip;
15
+ this.port = port;
16
+ this.username = username;
17
+ this.password = password;
18
+ this.keepalive = false;
19
+ logger_1.default.debug(`New ssh client: ${username}:${password}@${ip}:${port}`);
20
+ }
21
+ async open() {
22
+ return new Promise((resolve, reject) => {
23
+ this.close();
24
+ this.client = new ssh2_1.Client();
25
+ this.client.setNoDelay();
26
+ this.client.on('ready', () => {
27
+ this.client.removeAllListeners();
28
+ this.client.on('error', () => { });
29
+ resolve();
30
+ });
31
+ this.client.once('error', (err) => {
32
+ this.client.removeAllListeners();
33
+ reject(err);
34
+ });
35
+ try {
36
+ this.client.connect({
37
+ algorithms: {
38
+ cipher: ['aes256-gcm@openssh.com'],
39
+ compress: ['none'],
40
+ hmac: ['hmac-sha2-256-etm@openssh.com'],
41
+ kex: ['ecdh-sha2-nistp256'],
42
+ },
43
+ keepaliveInterval: 10000,
44
+ host: this.ip,
45
+ port: this.port,
46
+ username: this.username,
47
+ password: this.password,
48
+ });
49
+ }
50
+ catch (err) {
51
+ reject(err);
52
+ }
53
+ });
54
+ }
55
+ waitForStreamReadable(s) {
56
+ return new Promise((resolve) => {
57
+ s.once('readable', resolve);
58
+ });
59
+ }
60
+ async readStreamBytes(s, toRead) {
61
+ await this.waitForStreamReadable(s);
62
+ const data = s.read(toRead) ?? s.read();
63
+ if (data === null) {
64
+ return this.readStreamBytes(s, toRead);
65
+ }
66
+ const newToRead = toRead - data.length;
67
+ if (newToRead <= 0)
68
+ return data;
69
+ const newData = await this.readStreamBytes(s, newToRead);
70
+ return Buffer.concat([data, newData]);
71
+ }
72
+ async processStream(stream, info) {
73
+ stream.allowHalfOpen = false;
74
+ if (stream.readableFlowing)
75
+ stream.pause();
76
+ stream.on('error', () => {
77
+ try {
78
+ stream.removeAllListeners();
79
+ stream.once('error', () => { });
80
+ stream.end();
81
+ }
82
+ catch {
83
+ // do nothing
84
+ }
85
+ setTimeout(() => {
86
+ try {
87
+ stream.destroy();
88
+ }
89
+ catch {
90
+ // do nothing
91
+ }
92
+ }, 1000);
93
+ });
94
+ stream.on('close', () => {
95
+ stream.removeAllListeners();
96
+ });
97
+ const triggerStream = (type, ip) => {
98
+ stream.removeAllListeners();
99
+ this.emit(type, stream, info, ip);
100
+ };
101
+ let type;
102
+ let ip;
103
+ try {
104
+ const b = await this.readStreamBytes(stream, 50);
105
+ const s = b.toString('utf8').replace(/\s+/, ' ').split(' ');
106
+ if (s.length !== 2)
107
+ throw new Error(texts_1.ERR_WRONG_HANDSHAKE);
108
+ type = parseInt(s[0]);
109
+ ip = s[1];
110
+ }
111
+ catch (err) {
112
+ stream.emit('error', err);
113
+ return;
114
+ }
115
+ if (type === utils_1.SOCKET_TYPE_TLS) {
116
+ logger_1.default.debug((0, texts_1.LOG_DETECTED_STREAM)('TLS'));
117
+ triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_TLS, ip);
118
+ return;
119
+ }
120
+ if (type === utils_1.SOCKET_TYPE_HTTP1) {
121
+ logger_1.default.debug((0, texts_1.LOG_DETECTED_STREAM)('HTTP1'));
122
+ triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_HTTP1, ip);
123
+ return;
124
+ }
125
+ if (type === utils_1.SOCKET_TYPE_HTTP2) {
126
+ logger_1.default.debug((0, texts_1.LOG_DETECTED_STREAM)('HTTP2'));
127
+ triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_HTTP2, ip);
128
+ return;
129
+ }
130
+ if (type === utils_1.SOCKET_TYPE_TCP) {
131
+ logger_1.default.debug((0, texts_1.LOG_DETECTED_STREAM)('TCP'));
132
+ triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_TCP, ip);
133
+ return;
134
+ }
135
+ if (type === utils_1.SOCKET_TYPE_SSH) {
136
+ logger_1.default.debug((0, texts_1.LOG_DETECTED_STREAM)('SSH'));
137
+ triggerStream(utils_1.SSH_CLIENT_EVENT_STREAM_SSH, ip);
138
+ return;
139
+ }
140
+ logger_1.default.debug(texts_1.LOG_WRONG_STREAM);
141
+ stream.emit('error', new Error(texts_1.ERR_WRONG_STREAM));
142
+ }
143
+ forwardIn(port) {
144
+ return new Promise((resolve, reject) => {
145
+ this.client.on('tcp connection', async (info, accept) => {
146
+ logger_1.default.debug(texts_1.LOG_SSH_CONNECTION);
147
+ return this.processStream(accept(), info);
148
+ });
149
+ this.client.forwardIn('0.0.0.0', port, (err) => {
150
+ if (err)
151
+ reject(err);
152
+ else
153
+ resolve(err);
154
+ });
155
+ });
156
+ }
157
+ closeKeepAlive() {
158
+ this.keepalive = false;
159
+ this.close();
160
+ }
161
+ openKeepAlive() {
162
+ logger_1.default.debug(`SSH client open`);
163
+ this.keepalive = true;
164
+ this.open()
165
+ .then(() => {
166
+ logger_1.default.debug(`SSH client opened`);
167
+ this.emit(utils_1.SSH_CLIENT_EVENT_CONNECTED);
168
+ this.client.on('close', () => {
169
+ logger_1.default.debug(`SSH client closed`);
170
+ if (this.keepalive)
171
+ this.openKeepAlive();
172
+ });
173
+ })
174
+ .catch(() => {
175
+ logger_1.default.debug(`SSH client closed`);
176
+ this.emit(utils_1.SSH_CLIENT_EVENT_DISCONNECTED);
177
+ setTimeout(() => {
178
+ if (this.keepalive)
179
+ this.openKeepAlive();
180
+ }, 3000);
181
+ });
182
+ }
183
+ close() {
184
+ if (this.client) {
185
+ try {
186
+ this.client.removeAllListeners();
187
+ this.client.on('error', () => { });
188
+ this.client.end();
189
+ }
190
+ catch {
191
+ // nic nie rob
192
+ }
193
+ this.client = null;
194
+ }
195
+ }
196
+ }
197
+ exports.default = SshClient;