ssh_tunnel_proxy 1.2.11 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,16 +7,20 @@ In addition to the node api, a command line function called ssh-node2 is include
7
7
 
8
8
  ### Command line examples
9
9
 
10
+ Initialize ssh2 command:
11
+ ```
12
+ npm i ssh_tunnel_proxy -g
13
+ ```
10
14
  Connect to remote host and establish local forwards:
11
15
 
12
16
  ```
13
- ./ssh2-node -u=<username> -h=192.168.1.1 -k=~/.ssh/<private_key> -L=8180:192.168.1.1:80
17
+ ssh2-node -i=~/.ssh/<private_key> -L=8180:192.168.1.1:80 <username>@<hostname>
14
18
  ```
15
19
 
16
- Connect to host using parameters stored in ~/.config/ssh_tunnel_proxy/config.json:
20
+ Connect to host with parameters stored in ~/.config/ssh_tunnel_proxy/config.json:
17
21
 
18
22
  ```
19
- ./ssh2-node rh2
23
+ ssh2-node rh2
20
24
  ```
21
25
 
22
26
  default config file, located at:
@@ -35,11 +39,88 @@ default config file, located at:
35
39
  }]
36
40
  ```
37
41
 
38
- Execute a series of commands on remote host
42
+ Execute command on remote host
39
43
  ```
40
- ./ssh-node2 rh2 -e='uptime' -e='ls -all'
44
+ ssh2-node rh2 "ls -all"
41
45
  ```
42
46
 
47
+ ### List of command-line options (compatible with original ssh2 command):
48
+ ```
49
+ Usage: ssh2-node [options] [userhost] [exec...]
50
+
51
+ Nodejs ssh2 command line client
52
+
53
+ Arguments:
54
+ userhost Connect to hostname in config.
55
+ exec Command to exec on remote host (must be in quotes)
56
+
57
+ Options:
58
+ -V, --version output the version number
59
+ -2 --protocolv2 [protocolv2] Forces ssh to try protocol version 2 only.
60
+ -4 --ipv4only [ipv4only] Forces ssh to use IPv4 addresses only.
61
+ -6 --ipv6only [ipv6only] Forces ssh to use IPv6 addresses only.
62
+ -A --forward_enable [forward_enable] Enables forwarding of the authentication agent
63
+ connection.
64
+ -a --forward_disable [forward_disable] Disables forwarding of the authentication agent
65
+ connection.
66
+ -b --bind [bind] bind_address
67
+ -C --compress [compress] Requests compression of all data
68
+ -c --cipher [cipher] cipher_spec
69
+ -D --dynamic_forward [dynamic_forward] Specifies a local 'dynamic' application-level
70
+ port forwarding.
71
+ -e --escape [escape] escape_char Sets the escape character for
72
+ sessions with a pty.
73
+ -F --config [config] configfile
74
+ -f --background [background] Requests ssh to go to background just before
75
+ command execution.
76
+ -g --local_forward_remote [local_forward_remote] Allows remote hosts to connect to local forwarded
77
+ ports.
78
+ -I --smartcard [smartcard] smartcard_device Specify the device ssh.
79
+ -i --identity [identity] Private key filename.
80
+ -K --gssapi_auth [gssapi_auth] Enables GSSAPI-based authentication and
81
+ forwarding (delegation) of GSSAPI credentials to
82
+ the server.
83
+ -k --gssapi_disable [gssapi_disable] Disables forwarding (delegation) of GSSAPI
84
+ credentials to the server.
85
+ -L --local_forward_out [local_forward_out...] bind_address:port:host:hostport
86
+ -l --login_name [login_name] login_name
87
+ -M --master_mode [master_mode] Places the ssh client into 'master' mode for
88
+ connection sharing. Multiple -M options
89
+ -m --mac_spec [mac_spec] mac_spec
90
+ -N --no_exec [no_exec] Do not execute a remote command.
91
+ -n --redirect_stdin [redirect_stdin] Redirects stdin from /dev/null
92
+ -O --ctl_cmd [ctl_cmd] ctl_cmd
93
+ -o --option [option] option
94
+ -p --port [port] port Port to connect to on the remote host.
95
+ -q --quiet [quiet] Quiet mode. Causes most warning and diagnostic
96
+ messages to be suppressed.
97
+ -R --local_forward_in [local_forward_in...] bind_address:port:host:hostport
98
+ -S --ctl_path [ctl_path] ctl_path Specifies the location of a control
99
+ socket for connection sharing.
100
+ -s --subsystem [subsystem] May be used to request invocation of a subsystem
101
+ on the remote system.
102
+ -T --disable_tty [disable_tty] Disable pseudo-tty allocation.
103
+ -t --force_tty [force_tty] Force pseudo-tty allocation.
104
+ -V --version [version] Display the version number and exit.
105
+ -v --verbose [verbose] Verbose mode. Causes ssh to print debugging
106
+ messages about its progress.
107
+ -W --forward_stdin_stdout [forward_stdin_stdout] host:port Requests that standard input and output
108
+ on the client be forwarded to host on port ver
109
+ the secure channel.
110
+ -w --tunnel [tunnel] Requests tunnel device forwarding with the
111
+ specified tun(4) devices between the client
112
+ (local_tun) and the server (remote_tun).
113
+ -X --x11 [x11] Enables X11 forwarding.
114
+ -x --x11_disable [x11_disable] Disables X11 forwarding.
115
+ -Y --x11_trusted [x11_trusted] Enables trusted X11 forwarding.
116
+ -y --log [log] Send log information using the syslog(3)
117
+ -H --ngrok [ngrok] Obtain connectiion hostport from ngrok
118
+ -J --keychain_service [keychain_service] Service name to obtain private key from system
119
+ keychain.
120
+ -j --keychain_account [keychain_account] Account name to obtain private key from system
121
+ keychain.
122
+ -h, --help display help for command
123
+ ```
43
124
  ### Api examples
44
125
 
45
126
  Exec remote commands using async await and processing result through streams.
@@ -0,0 +1,65 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ /// <reference types="node" />
5
+ import { Server, Socket } from 'net';
6
+ import { Writable } from 'stream';
7
+ import { EventEmitter } from 'events';
8
+ import { Client, Channel } from 'electron-ssh2';
9
+ import { KeypairStorage } from './keypair_storage';
10
+ import { NgrokApi } from './ngrok_service';
11
+ import { main } from './ssh2-node';
12
+ interface Listener {
13
+ [key: string]: Server;
14
+ }
15
+ export type SSHConfig = {
16
+ hostname: string;
17
+ username: string;
18
+ password?: string;
19
+ host?: string;
20
+ port?: string;
21
+ proxy_ports?: Array<string>;
22
+ remote_ports?: Array<string>;
23
+ private_key?: string | null;
24
+ private_key_filename?: string | null;
25
+ disabled?: boolean;
26
+ server_name?: string;
27
+ service_name?: string;
28
+ compress?: string;
29
+ cypher?: string;
30
+ exec?: Array<string>;
31
+ shell?: boolean;
32
+ ngrok_api?: string;
33
+ keepaliveInterval?: number;
34
+ };
35
+ export declare class SSHTunnelProxy extends EventEmitter {
36
+ protected client: Client;
37
+ protected keypairStorage: KeypairStorage;
38
+ protected config: SSHConfig;
39
+ protected listener: Listener;
40
+ protected retries: number;
41
+ protected networkOnline: boolean;
42
+ protected _tunnelReadyTimeout: ReturnType<typeof setTimeout>;
43
+ debug_en: boolean;
44
+ debug_ssh: boolean;
45
+ constructor();
46
+ protected close_sockets(proxy_ports: Array<string>): void;
47
+ protected setup_ssh_forward(socket: Socket, remote_hostname: string, remote_port: string): void;
48
+ setupProxyPorts(proxy_ports: Array<string>): Promise<void>;
49
+ execCmd(cmd: string, dataStream?: Writable, errStream?: Writable): Promise<void>;
50
+ protected remote_shell(err: Error, stream: Channel): void;
51
+ protected cleanup_events(client: Client): void;
52
+ do_ssh_connect(opts: SSHConfig): Promise<void>;
53
+ validate_port_number(port: number, whitelist: object): boolean;
54
+ validate_local_forward(proxy_ports: Array<string>, whitelist: object): boolean;
55
+ protected ssh_retry_connect(opts: SSHConfig): void;
56
+ protected ssh_start_tunnel(opts: SSHConfig): Promise<unknown>;
57
+ connectSSH(opts: SSHConfig, whitelist: object | null): Promise<void>;
58
+ onNetworkOnline(): void;
59
+ onNetworkOffline(): void;
60
+ generateAndStoreKeypair(serviceName: string, account: string): void;
61
+ getPublicKey(serviceName: string, account: string): void;
62
+ getClient(): Client;
63
+ protected debug(...args: any[]): void;
64
+ }
65
+ export { KeypairStorage, NgrokApi, main as sshcli };
@@ -0,0 +1,496 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
+ return new (P || (P = Promise))(function (resolve, reject) {
20
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
24
+ });
25
+ };
26
+ var __generator = (this && this.__generator) || function (thisArg, body) {
27
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
28
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
+ function verb(n) { return function (v) { return step([n, v]); }; }
30
+ function step(op) {
31
+ if (f) throw new TypeError("Generator is already executing.");
32
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
33
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
34
+ if (y = 0, t) op = [op[0] & 2, t.value];
35
+ switch (op[0]) {
36
+ case 0: case 1: t = op; break;
37
+ case 4: _.label++; return { value: op[1], done: false };
38
+ case 5: _.label++; y = op[1]; op = [0]; continue;
39
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
+ default:
41
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
+ if (t[2]) _.ops.pop();
46
+ _.trys.pop(); continue;
47
+ }
48
+ op = body.call(thisArg, _);
49
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
+ }
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.sshcli = exports.NgrokApi = exports.KeypairStorage = exports.SSHTunnelProxy = void 0;
55
+ var net_1 = require("net");
56
+ var fs_1 = require("fs");
57
+ var process_1 = require("process");
58
+ var events_1 = require("events");
59
+ var electron_ssh2_1 = require("electron-ssh2");
60
+ var keypair_storage_1 = require("./keypair_storage");
61
+ Object.defineProperty(exports, "KeypairStorage", { enumerable: true, get: function () { return keypair_storage_1.KeypairStorage; } });
62
+ var ngrok_service_1 = require("./ngrok_service");
63
+ Object.defineProperty(exports, "NgrokApi", { enumerable: true, get: function () { return ngrok_service_1.NgrokApi; } });
64
+ var ssh2_node_1 = require("./ssh2-node");
65
+ Object.defineProperty(exports, "sshcli", { enumerable: true, get: function () { return ssh2_node_1.main; } });
66
+ var os_1 = require("os");
67
+ var SSHTunnelProxy = (function (_super) {
68
+ __extends(SSHTunnelProxy, _super);
69
+ function SSHTunnelProxy() {
70
+ var _this = _super.call(this) || this;
71
+ _this.listener = {};
72
+ _this.retries = 0;
73
+ _this.debug_en = false;
74
+ _this.debug_ssh = false;
75
+ _this._tunnelReadyTimeout = undefined;
76
+ _this.networkOnline = false;
77
+ _this.config = null;
78
+ _this.client = new electron_ssh2_1.Client();
79
+ _this.keypairStorage = new keypair_storage_1.KeypairStorage();
80
+ return _this;
81
+ }
82
+ SSHTunnelProxy.prototype.close_sockets = function (proxy_ports) {
83
+ var _this = this;
84
+ if (!proxy_ports)
85
+ return;
86
+ proxy_ports.forEach(function (proxy_port) {
87
+ var server = _this.listener[proxy_port];
88
+ if (server && server.listening) {
89
+ _this.debug_en && _this.debug('SSH Server :: closing forward:', proxy_port);
90
+ server.close();
91
+ delete _this.listener[proxy_port];
92
+ }
93
+ });
94
+ };
95
+ SSHTunnelProxy.prototype.setup_ssh_forward = function (socket, remote_hostname, remote_port) {
96
+ var _this = this;
97
+ var on_setup_ssh_forward = function (err, stream) {
98
+ if (err) {
99
+ _this.emit('debug', err);
100
+ _this.debug_en && _this.debug('socket forward error:', err);
101
+ return;
102
+ }
103
+ stream.on('end', function () {
104
+ socket.resume();
105
+ });
106
+ stream.pipe(socket).pipe(stream);
107
+ var shutdown_forward = function () {
108
+ stream.unpipe(socket);
109
+ socket.unpipe(stream);
110
+ stream.end();
111
+ };
112
+ socket.on('close', function () {
113
+ shutdown_forward();
114
+ });
115
+ socket.on('error', function (err) {
116
+ _this.emit('debug', err);
117
+ _this.debug_en && _this.debug('socket on error:', err);
118
+ shutdown_forward();
119
+ });
120
+ };
121
+ this.client.forwardOut(socket.remoteAddress, socket.remotePort, remote_hostname, remote_port, on_setup_ssh_forward);
122
+ };
123
+ SSHTunnelProxy.prototype.setupProxyPorts = function (proxy_ports) {
124
+ var _this = this;
125
+ return new Promise(function (resolve, reject) {
126
+ if (!proxy_ports) {
127
+ resolve();
128
+ return;
129
+ }
130
+ var listeners = 0;
131
+ proxy_ports.forEach(function (proxy_port) {
132
+ var _a = proxy_port.split(':'), local_port = _a[0], remote_hostname = _a[1], remote_port = _a[2];
133
+ var server = _this.listener[proxy_port];
134
+ if (server && server.listening) {
135
+ _this.debug_en && _this.debug('SSH Server :: closing forward 2:', proxy_port);
136
+ server.close();
137
+ }
138
+ _this.listener[proxy_port] = (0, net_1.createServer)({ allowHalfOpen: false }, function (socket) {
139
+ if (_this.debug_en) {
140
+ var debug_msg = 'SSH Server :: connection on ' + local_port + ' ' + socket.remotePort;
141
+ _this.emit('debug', debug_msg);
142
+ _this.debug(debug_msg);
143
+ }
144
+ _this.setup_ssh_forward(socket, remote_hostname, remote_port);
145
+ });
146
+ try {
147
+ var status_msg = 'SSH Server :: before listen on ' + local_port;
148
+ _this.emit('debug', status_msg);
149
+ _this.listener[proxy_port].listen(local_port, function () {
150
+ var status_msg = 'SSH Server :: bound on ' + local_port;
151
+ _this.emit('debug', status_msg);
152
+ if (listeners++ >= proxy_ports.length - 1) {
153
+ _this.emit('ssh_tunnel_ready', {});
154
+ if (_this._tunnelReadyTimeout) {
155
+ clearTimeout(_this._tunnelReadyTimeout);
156
+ _this._tunnelReadyTimeout = undefined;
157
+ }
158
+ resolve();
159
+ }
160
+ });
161
+ }
162
+ catch (err) {
163
+ _this.debug_en && _this.debug('listen err:', err);
164
+ reject(err);
165
+ }
166
+ });
167
+ });
168
+ };
169
+ SSHTunnelProxy.prototype.execCmd = function (cmd, dataStream, errStream) {
170
+ var _this = this;
171
+ return new Promise(function (resolve, reject) {
172
+ var remote_exec = function (err, stream) {
173
+ if (err) {
174
+ reject(err);
175
+ return;
176
+ }
177
+ var onClose = function () {
178
+ stream.removeListener('close', onClose);
179
+ resolve();
180
+ };
181
+ stream.on('close', onClose);
182
+ stream.on('data', function (data) {
183
+ if (dataStream)
184
+ dataStream.write(data);
185
+ else
186
+ process_1.stdout.write(data);
187
+ });
188
+ stream.stderr.on('data', function (data) {
189
+ if (errStream)
190
+ errStream.write(data);
191
+ else
192
+ process_1.stderr.write(data);
193
+ });
194
+ };
195
+ _this.client.exec(cmd, remote_exec);
196
+ });
197
+ };
198
+ SSHTunnelProxy.prototype.remote_shell = function (err, stream) {
199
+ var _this = this;
200
+ if (err)
201
+ throw err;
202
+ process_1.stdin.setRawMode(true);
203
+ var stdinData = function (data) {
204
+ stream.stdin.write(data);
205
+ };
206
+ process_1.stdin.on('data', stdinData);
207
+ var stdoutData = function (data) {
208
+ process_1.stdout.write(data);
209
+ };
210
+ stream.stdout.on('data', stdoutData);
211
+ stream
212
+ .on('close', function () {
213
+ process_1.stdin.setRawMode(false);
214
+ process_1.stdin.removeListener('data', stdinData);
215
+ stream.stdout.removeListener('data', stdoutData);
216
+ (0, process_1.exit)();
217
+ })
218
+ .stderr.on('data', function (data) {
219
+ _this.debug_en && _this.debug('shell' + data);
220
+ });
221
+ };
222
+ SSHTunnelProxy.prototype.cleanup_events = function (client) {
223
+ var eventNames = ['close', 'end', 'error', 'greeting', 'handshake', 'ready'];
224
+ for (var j = 0; j < eventNames.length; j++) {
225
+ var eventName = eventNames[j];
226
+ var events = client._events[eventName];
227
+ if (events) {
228
+ if (events instanceof Array) {
229
+ for (var i = 0; i < events.length; i++) {
230
+ client.removeListener(eventName, events[i]);
231
+ }
232
+ }
233
+ if (events instanceof Function) {
234
+ client.removeListener(eventName, events);
235
+ }
236
+ }
237
+ }
238
+ };
239
+ SSHTunnelProxy.prototype.do_ssh_connect = function (opts) {
240
+ var _this = this;
241
+ var _client = this.client;
242
+ return new Promise(function (resolve) {
243
+ _this.close_sockets(opts.proxy_ports);
244
+ _this.cleanup_events(_client);
245
+ _client.on('ready', function () { return __awaiter(_this, void 0, void 0, function () {
246
+ var i, cmd;
247
+ return __generator(this, function (_a) {
248
+ switch (_a.label) {
249
+ case 0:
250
+ if (this._tunnelReadyTimeout) {
251
+ clearTimeout(this._tunnelReadyTimeout);
252
+ delete this._tunnelReadyTimeout;
253
+ }
254
+ if (!opts.proxy_ports) return [3, 2];
255
+ return [4, this.setupProxyPorts(opts.proxy_ports)];
256
+ case 1:
257
+ _a.sent();
258
+ _a.label = 2;
259
+ case 2:
260
+ if (!opts.shell) return [3, 3];
261
+ this.client.shell(this.remote_shell);
262
+ return [3, 7];
263
+ case 3:
264
+ if (!(opts.exec && opts.exec.length > 0)) return [3, 7];
265
+ i = 0;
266
+ _a.label = 4;
267
+ case 4:
268
+ if (!(i < opts.exec.length)) return [3, 7];
269
+ cmd = opts.exec[i];
270
+ this.debug_en && this.debug(opts.username + '@' + opts.hostname + ':' + cmd);
271
+ return [4, this.execCmd(cmd)];
272
+ case 5:
273
+ _a.sent();
274
+ _a.label = 6;
275
+ case 6:
276
+ i++;
277
+ return [3, 4];
278
+ case 7:
279
+ resolve();
280
+ return [2];
281
+ }
282
+ });
283
+ }); });
284
+ _client.on('end', function () {
285
+ _this.debug_en && _this.debug('SSH Client :: end');
286
+ _this.close_sockets(opts.proxy_ports);
287
+ });
288
+ _client.on('close', function () {
289
+ _this.debug_en && _this.debug('SSH Client :: close');
290
+ _this.close_sockets(opts.proxy_ports);
291
+ });
292
+ _client.on('error', function (err) {
293
+ _this.debug_en && _this.debug('SSH Client :: error :: ' + err);
294
+ _this.emit('debug', err);
295
+ if (_this._tunnelReadyTimeout) {
296
+ clearTimeout(_this._tunnelReadyTimeout);
297
+ _this._tunnelReadyTimeout = undefined;
298
+ }
299
+ _this.ssh_retry_connect(opts);
300
+ resolve();
301
+ });
302
+ var config = JSON.parse(JSON.stringify(opts));
303
+ config.debug = _this.debug_ssh
304
+ ? function () {
305
+ var args = [];
306
+ for (var _i = 0; _i < arguments.length; _i++) {
307
+ args[_i] = arguments[_i];
308
+ }
309
+ console.log.apply(console, args);
310
+ }
311
+ : null;
312
+ config.keepaliveInterval = opts.keepaliveInterval || 10000;
313
+ var ssh_tunnel_extensions = [
314
+ 'alias',
315
+ 'disabled',
316
+ 'hostname',
317
+ 'ngrok_api',
318
+ 'server_name',
319
+ 'service_name',
320
+ 'whitelist',
321
+ 'private_key_filename',
322
+ ];
323
+ for (var i = 0; i < ssh_tunnel_extensions.length; i++)
324
+ delete config[ssh_tunnel_extensions[i]];
325
+ _client.connect(config);
326
+ });
327
+ };
328
+ SSHTunnelProxy.prototype.validate_port_number = function (port, whitelist) {
329
+ if (isNaN(port))
330
+ return false;
331
+ if (port < 1 || port > 65535)
332
+ return false;
333
+ if (port < 1024 && whitelist && whitelist[port] === undefined)
334
+ return false;
335
+ return true;
336
+ };
337
+ SSHTunnelProxy.prototype.validate_local_forward = function (proxy_ports, whitelist) {
338
+ var _this = this;
339
+ if (!proxy_ports)
340
+ return;
341
+ var local_ports = [];
342
+ var remote_ports = [];
343
+ proxy_ports.forEach(function (proxy_port) {
344
+ var _a = proxy_port.split(':'), local_port = _a[0], remote_hostname = _a[1], remote_port = _a[2];
345
+ var ilocal_port = parseInt(local_port);
346
+ var iremote_port = parseInt(remote_port);
347
+ if (remote_hostname.length < 1)
348
+ remote_ports.push(remote_port);
349
+ if (!_this.validate_port_number(ilocal_port, whitelist))
350
+ local_ports.push(local_port);
351
+ if (!_this.validate_port_number(iremote_port, whitelist))
352
+ remote_ports.push(remote_port);
353
+ });
354
+ if (local_ports.length || remote_ports.length) {
355
+ var err = new Error('Invalid local forward');
356
+ this.debug_en &&
357
+ this.debug('invalid ports found:\n', JSON.stringify(proxy_ports, null, 2), '\n', JSON.stringify(whitelist, null, 2));
358
+ throw err;
359
+ }
360
+ return true;
361
+ };
362
+ SSHTunnelProxy.prototype.ssh_retry_connect = function (opts) {
363
+ var _this = this;
364
+ var invoke = function () {
365
+ _this.connectSSH(opts, null);
366
+ };
367
+ if (this.retries++ < 10)
368
+ this._tunnelReadyTimeout = setTimeout(invoke, 5000);
369
+ };
370
+ SSHTunnelProxy.prototype.ssh_start_tunnel = function (opts) {
371
+ return __awaiter(this, void 0, void 0, function () {
372
+ var do_ssh_connect;
373
+ var _this = this;
374
+ return __generator(this, function (_a) {
375
+ do_ssh_connect = function (resolve) { return __awaiter(_this, void 0, void 0, function () {
376
+ var _this = this;
377
+ return __generator(this, function (_a) {
378
+ switch (_a.label) {
379
+ case 0: return [4, this.do_ssh_connect(opts).then(function () {
380
+ var hostport = opts.host + ':' + opts.port;
381
+ if (_this.debug_en) {
382
+ var msg = 'SSH connection to ' + opts.server_name + ' established at ' + hostport;
383
+ _this.debug(msg);
384
+ }
385
+ _this.emit('status', '', 'ready', hostport);
386
+ _this.retries = 0;
387
+ resolve();
388
+ })];
389
+ case 1:
390
+ _a.sent();
391
+ return [2];
392
+ }
393
+ });
394
+ }); };
395
+ return [2, new Promise(do_ssh_connect)];
396
+ });
397
+ });
398
+ };
399
+ SSHTunnelProxy.prototype.connectSSH = function (opts, whitelist) {
400
+ return __awaiter(this, void 0, void 0, function () {
401
+ var _opts, _a, ngrokApi, hostport, err;
402
+ var _this = this;
403
+ return __generator(this, function (_b) {
404
+ switch (_b.label) {
405
+ case 0:
406
+ _opts = JSON.parse(JSON.stringify(opts));
407
+ _opts.whitelist = whitelist !== undefined ? whitelist : _opts.whitelist ? _opts.whitelist : null;
408
+ try {
409
+ this.validate_local_forward(_opts.proxy_ports, _opts.whitelist);
410
+ }
411
+ catch (err) {
412
+ this.emit('debug', err);
413
+ this.debug_en && this.debug(err);
414
+ throw (err);
415
+ }
416
+ if (opts.password && !opts.password.length) {
417
+ _opts.password = undefined;
418
+ }
419
+ if (_opts.private_key_filename) {
420
+ try {
421
+ if (_opts.private_key_filename[0] == '~') {
422
+ _opts.private_key_filename = os_1.homedir + _opts.private_key_filename.substr(1);
423
+ }
424
+ _opts.privateKey = (0, fs_1.readFileSync)(_opts.private_key_filename).toString();
425
+ }
426
+ catch (err) {
427
+ console.log('Error loading key:', err);
428
+ throw (err);
429
+ }
430
+ }
431
+ if (!(_opts.service_name && _opts.server_name)) return [3, 2];
432
+ _a = _opts;
433
+ return [4, this.keypairStorage.get_keypair(opts.service_name, opts.server_name)];
434
+ case 1:
435
+ _a.privateKey = _b.sent();
436
+ return [3, 3];
437
+ case 2:
438
+ if (!_opts.server_name)
439
+ _opts.server_name = 'sshtun';
440
+ _b.label = 3;
441
+ case 3:
442
+ if (!_opts.ngrok_api) return [3, 5];
443
+ ngrokApi = new ngrok_service_1.NgrokApi(_opts.ngrok_api);
444
+ return [4, ngrokApi.get_hostport().catch(function () {
445
+ _this.debug_en && _this.debug('ngrok get_hostport connection error');
446
+ _this.ssh_retry_connect(_opts);
447
+ })];
448
+ case 4:
449
+ hostport = _b.sent();
450
+ if (hostport && hostport.host) {
451
+ _opts.host = hostport.host;
452
+ _opts.port = hostport.port;
453
+ }
454
+ else {
455
+ err = new Error('invalid hostport obtained from ngrok api');
456
+ throw (err);
457
+ }
458
+ _b.label = 5;
459
+ case 5:
460
+ this.debug_en && this.debug('connectSSH.ts:\n', JSON.stringify(_opts, null, 2));
461
+ return [4, this.ssh_start_tunnel(_opts).catch(function (err) {
462
+ _this.debug_en && _this.debug('ssh_start_tunnel catch:' + err);
463
+ })];
464
+ case 6:
465
+ _b.sent();
466
+ return [2];
467
+ }
468
+ });
469
+ });
470
+ };
471
+ SSHTunnelProxy.prototype.onNetworkOnline = function () {
472
+ this.networkOnline = true;
473
+ };
474
+ SSHTunnelProxy.prototype.onNetworkOffline = function () {
475
+ this.networkOnline = false;
476
+ };
477
+ SSHTunnelProxy.prototype.generateAndStoreKeypair = function (serviceName, account) {
478
+ this.keypairStorage.generate_and_store_keypair(serviceName, account);
479
+ };
480
+ SSHTunnelProxy.prototype.getPublicKey = function (serviceName, account) {
481
+ this.keypairStorage.get_public_key_from_keychain(serviceName, account);
482
+ };
483
+ SSHTunnelProxy.prototype.getClient = function () {
484
+ return this.client;
485
+ };
486
+ SSHTunnelProxy.prototype.debug = function () {
487
+ var args = [];
488
+ for (var _i = 0; _i < arguments.length; _i++) {
489
+ args[_i] = arguments[_i];
490
+ }
491
+ console.log.apply(console, args);
492
+ };
493
+ return SSHTunnelProxy;
494
+ }(events_1.EventEmitter));
495
+ exports.SSHTunnelProxy = SSHTunnelProxy;
496
+ //# sourceMappingURL=index.js.map