ssh_tunnel_proxy 1.2.8 → 1.2.9

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
@@ -62,6 +62,32 @@ Start a terminal session on the remote host:
62
62
  node main.js -c -S
63
63
  ```
64
64
 
65
+ Example use of api to exec remote commands using async await and processing result through streams. Complete example is in test/test_remote_exec_streams.js:
66
+
67
+ ```js
68
+ async function test_exec_stream(opts) {
69
+ sshTunnelProxy.debug_en = true;
70
+ await sshTunnelProxy.connectSSH(opts);
71
+
72
+ // exec remote command and save result to string
73
+ const uptime_result = await sshTunnelProxy.execCmd('uptime');
74
+ console.log('uptime:' + uptime_result.toString());
75
+
76
+ // exec remote command and pipe data through tunnel until end of data
77
+ const tunnel = new PassThrough();
78
+ tunnel.pipe(split())
79
+ .pipe(to_ls_JSON)
80
+ .pipe(to_JSON_string)
81
+ .pipe(process.stdout);
82
+
83
+ await sshTunnelProxy.execCmd('ls -all', tunnel);
84
+
85
+ // stream processing complete
86
+ console.log('done');
87
+ process.exit();
88
+ }
89
+ ```
90
+
65
91
  The following code is an example of use of the api with electronjs.
66
92
 
67
93
  main.js:
package/lib/index.js CHANGED
@@ -102,63 +102,67 @@ class SSHTunnelProxy extends Client {
102
102
  }
103
103
 
104
104
  // function to setup proxy forwards for ssh tunnel
105
- on_ssh_client_ready(server_name, proxy_ports, resolve) {
105
+ setupProxyPorts(server_name, proxy_ports) {
106
106
 
107
- var listeners = 0;
108
- if (this.listener[server_name]) this.listener[server_name].isConnected = true;
109
- const _this = this;
107
+ return new Promise((resolve, reject) => {
110
108
 
111
- // iterate through a list of local forward ports and create a local proxy port
112
- proxy_ports.forEach(proxy_port => {
109
+ var listeners = 0;
110
+ if (this.listener[server_name]) this.listener[server_name].isConnected = true;
111
+ const _this = this;
113
112
 
114
- const [local_port, remote_hostname, remote_port] = proxy_port.split(':');
113
+ // iterate through a list of local forward ports and create a local proxy port
114
+ proxy_ports.forEach(proxy_port => {
115
115
 
116
- // create local socket server
117
- const server = this.listener[server_name][proxy_port];
118
- if (server && server.listening) {
119
- this.debug_en && this.debug('SSH Server :: closing forward 2:', proxy_port)
120
- server.close();
121
- }
122
- this.listener[server_name][proxy_port] = null;
123
- this.listener[server_name][proxy_port] = net.createServer({ keepAlive: true, allowHalfOpen: false }, socket => {
116
+ const [local_port, remote_hostname, remote_port] = proxy_port.split(':');
124
117
 
125
- if (this.debug_en) {
126
- var debug_msg = 'SSH Server :: connection on ' + local_port + ' ' + socket.remotePort;
127
- _this.emit('debug', debug_msg);
128
- _this.debug(debug_msg);
118
+ // create local socket server
119
+ const server = this.listener[server_name][proxy_port];
120
+ if (server && server.listening) {
121
+ this.debug_en && this.debug('SSH Server :: closing forward 2:', proxy_port)
122
+ server.close();
129
123
  }
124
+ this.listener[server_name][proxy_port] = null;
125
+ this.listener[server_name][proxy_port] = net.createServer({ keepAlive: true, allowHalfOpen: false }, socket => {
130
126
 
131
- // create a proxy forward between local and remote ports
132
- _this.setup_ssh_forward(socket, remote_hostname, remote_port);
133
- });
127
+ if (this.debug_en) {
128
+ var debug_msg = 'SSH Server :: connection on ' + local_port + ' ' + socket.remotePort;
129
+ _this.emit('debug', debug_msg);
130
+ _this.debug(debug_msg);
131
+ }
134
132
 
135
- // start listening on port
136
- try {
137
- var status_msg = 'SSH Server :: before listen on ' + local_port;
138
- _this.emit('debug', status_msg);
139
- _this.listener[server_name][proxy_port].listen(local_port, () => {
133
+ // create a proxy forward between local and remote ports
134
+ _this.setup_ssh_forward(socket, remote_hostname, remote_port);
135
+ });
140
136
 
141
- // emit server listening on port message
142
- var status_msg = 'SSH Server :: bound on ' + local_port;
137
+ // start listening on port
138
+ try {
139
+ var status_msg = 'SSH Server :: before listen on ' + local_port;
143
140
  _this.emit('debug', status_msg);
144
-
145
- // if all listeners have been successfully established, resolve setup connection
146
- if (listeners++ >= proxy_ports.length - 1) {
147
- _this.emit('ssh_tunnel_ready', {});
148
- if (_this._tunnelReadyTimeout) {
149
- clearTimeout(this._tunnelReadyTimeout);
150
- _this._tunnelReadyTimeout = undefined
141
+ _this.listener[server_name][proxy_port].listen(local_port, () => {
142
+
143
+ // emit server listening on port message
144
+ var status_msg = 'SSH Server :: bound on ' + local_port;
145
+ _this.emit('debug', status_msg);
146
+
147
+ // if all listeners have been successfully established, resolve setup connection
148
+ if (listeners++ >= proxy_ports.length - 1) {
149
+ _this.emit('ssh_tunnel_ready', {});
150
+ if (_this._tunnelReadyTimeout) {
151
+ clearTimeout(this._tunnelReadyTimeout);
152
+ _this._tunnelReadyTimeout = undefined
153
+ }
154
+ resolve();
151
155
  }
152
- resolve();
153
- }
154
- });
155
- } catch (err) {
156
- _this.debug_en && _this.debug('listen err:', err);
157
- }
156
+ });
157
+ } catch (err) {
158
+ _this.debug_en && _this.debug('listen err:', err);
159
+ reject(err);
160
+ }
161
+ });
158
162
  });
159
163
  }
160
164
 
161
- exec_cmd(cmd, dataStream, errStream) {
165
+ execCmd(cmd, dataStream, errStream) {
162
166
 
163
167
  return new Promise((resolve, reject) => {
164
168
  var bufs = [];
@@ -255,13 +259,13 @@ class SSHTunnelProxy extends Client {
255
259
  if (opts.shell) {
256
260
  _this.shell(_this.remote_shell);
257
261
 
258
- // if exec requested, exec series of cmds
262
+ // if exec requested, exec series of cmds
259
263
  } else {
260
264
  if (opts.exec && opts.exec.length > 0) {
261
265
  for (var i = 0; i < opts.exec.length; i++) {
262
266
  var cmd = opts.exec[i];
263
267
  console.log(opts.username + '@' + opts.hostname + ':' + cmd);
264
- var result = await _this.exec_cmd(cmd);
268
+ var result = await _this.execCmd(cmd);
265
269
  console.log(result.toString());
266
270
  }
267
271
  process.exit();
@@ -270,10 +274,9 @@ class SSHTunnelProxy extends Client {
270
274
 
271
275
  // if local forward requested, setup local forwarding ports to remote host
272
276
  if (opts.proxy_ports) {
273
- _this.on_ssh_client_ready(opts.server_name, opts.proxy_ports, resolve);
274
- } else {
275
- resolve();
277
+ await _this.setupProxyPorts(opts.server_name, opts.proxy_ports);
276
278
  }
279
+ resolve();
277
280
  });
278
281
 
279
282
  _this.on('end', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssh_tunnel_proxy",
3
- "version": "1.2.8",
3
+ "version": "1.2.9",
4
4
  "description": "obtain ngrok host port from api and establish a ssh connection",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -18,7 +18,9 @@
18
18
  "net": "^1.0.2",
19
19
  "ngrok": "^4.3.3",
20
20
  "socksv5": "^0.0.6",
21
- "sshpk": "^1.17.0"
21
+ "split": "^1.0.1",
22
+ "sshpk": "^1.17.0",
23
+ "through": "^2.3.8"
22
24
  },
23
25
  "devDependencies": {
24
26
  "eslint": "^8.33.0",
@@ -0,0 +1,140 @@
1
+ /*
2
+
3
+ test remote exec streams
4
+
5
+ */
6
+
7
+ //const assert = require('assert');
8
+ //const { describe, it } = require('mocha');
9
+ const process = require('process');
10
+ const fs = require('fs');
11
+ const { PassThrough } = require("stream");
12
+ const through = require('through');
13
+ const split = require('split');
14
+
15
+ const { SSHTunnelProxy } = require('../lib/index.js');
16
+
17
+ // get config file containing api keys
18
+ const homedir = require('os').homedir();
19
+ var config = null;
20
+ try {
21
+ config = JSON.parse(fs.readFileSync(homedir + '/.config/ssh_tunnel_proxy/config.json'), 'utf8');
22
+ } catch (err) {
23
+ console.log('Error reading config');
24
+ process.exit();
25
+ }
26
+
27
+ if (!config && !config[1]) {
28
+ console.log('Specified config not found in configs');
29
+ process.exit();
30
+ }
31
+
32
+ const opts = config[1];
33
+
34
+ const sshTunnelProxy = new SSHTunnelProxy();
35
+
36
+ // parse shell command result into array of strings
37
+ function parse_cmd(str) {
38
+
39
+ let result = [];
40
+ let log_matches = false;
41
+
42
+ let regex = /(([\w-/_~]+)|("(.*?)")|('(.*?)'))/g;
43
+ let groups = [2, 4, 6];
44
+ let match;
45
+
46
+ while ((match = regex.exec(str)) !== null) {
47
+ // This is necessary to avoid infinite loops
48
+ // with zero-width matches
49
+ if (match.index === regex.lastIndex) {
50
+ regex.lastIndex++;
51
+ }
52
+
53
+ // For this to work the regex groups need to
54
+ // be mutually exclusive
55
+ groups.forEach(function (group) {
56
+ if (match[group]) {
57
+ result.push(match[group]);
58
+ }
59
+ });
60
+
61
+ // show matches for debugging
62
+ log_matches && match.forEach(function (m, group) {
63
+ if (m) {
64
+ console.log(`Match '${m}' found in group: ${group}`);
65
+ }
66
+ });
67
+ }
68
+ return result;
69
+ }
70
+
71
+ // generate object from arrays of names/values
72
+ function toObj(names, values) {
73
+ const obj = {};
74
+ for (var i = 0; i < names.length; i++) {
75
+ obj[names[i]] = values[i];
76
+ }
77
+ return obj;
78
+ }
79
+
80
+ // stream that receives single line of ls long data and converts to json object
81
+ const to_ls_JSON = new through(function (data) {
82
+ const file = processLSLong(data);
83
+ if (file.name) this.queue(file);
84
+ });
85
+
86
+ // stream that receives objects and converts to JSON strings
87
+ const to_JSON_string = new through(function (data) {
88
+ this.queue(JSON.stringify(data) + '\n');
89
+ });
90
+
91
+ // parse ls long line output into json object
92
+ function processLSLong(line) {
93
+ const format10 = ['pm', 'links', 'user', 'group', 'size', 'M', 'D', 'H', 'MM', 'name'];
94
+ const format9 = ['pm', 'links', 'user', 'group', 'size', 'M', 'D', 'Y', 'name'];
95
+ const values = parse_cmd(line);
96
+ const obj = toObj((values.length < 10) ? format9 : format10, values);
97
+ if (obj.pm) {
98
+ obj.type = obj.pm.substring(0, 1);
99
+ obj.pm = obj.pm.substring(1);
100
+ }
101
+ if (obj.M && obj.D) {
102
+ obj.date = obj.M + ' ' + obj.D;
103
+ delete obj.M;
104
+ delete obj.D;
105
+ if (obj.Y) {
106
+ obj.date += ' ' + obj.Y;
107
+ delete obj.Y;
108
+ }
109
+ }
110
+ if (obj.H && obj.MM) {
111
+ obj.time = obj.H + ':' + obj.MM;
112
+ delete obj.H;
113
+ delete obj.MM;
114
+ }
115
+ return obj;
116
+ }
117
+
118
+ async function test_exec_stream() {
119
+ sshTunnelProxy.debug_en = true;
120
+ await sshTunnelProxy.connectSSH(opts);
121
+
122
+ // exec remote command and save result to string
123
+ const uptime_result = await sshTunnelProxy.execCmd('uptime');
124
+ console.log('uptime:' + uptime_result.toString());
125
+
126
+ // exec remote command and pipe data through tunnel until no data
127
+ const tunnel = new PassThrough();
128
+ tunnel.pipe(split())
129
+ .pipe(to_ls_JSON)
130
+ .pipe(to_JSON_string)
131
+ .pipe(process.stdout);
132
+
133
+ await sshTunnelProxy.execCmd('ls -all', tunnel);
134
+
135
+ // stream processing complete
136
+ console.log('done');
137
+ process.exit();
138
+ }
139
+
140
+ test_exec_stream();