ssh_tunnel_proxy 1.2.9 → 1.2.10

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,30 +62,47 @@ 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:
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.js:
66
66
 
67
67
  ```js
68
- async function test_exec_stream(opts) {
69
- sshTunnelProxy.debug_en = true;
70
- await sshTunnelProxy.connectSSH(opts);
68
+ // lsLongShellProc to json stream test
69
+ async function lsTest(sshTunnelProxy) {
70
+ return new Promise(async (resolve) => {
71
+
72
+ // exec remote command and pipe data through tunnel until end of data
73
+ const tunnel = new PassThrough();
74
+ pipeline(tunnel,
75
+ split(),
76
+ to_lsParse(),
77
+ to_JSONString(),
78
+ process.stdout,
79
+ () => { }
80
+ );
81
+
82
+ const lscmd = 'ls -all';
83
+ console.log('\ninvoking ' + lscmd + ' on remote host:\n');
84
+ await sshTunnelProxy.execCmd(lscmd, tunnel);
85
+
86
+ // stream processing complete
87
+ console.log('ls -all completed');
88
+ resolve();
89
+ })
90
+ }
71
91
 
72
- // exec remote command and save result to string
73
- const uptime_result = await sshTunnelProxy.execCmd('uptime');
74
- console.log('uptime:' + uptime_result.toString());
92
+ async function runTests() {
75
93
 
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);
94
+ const sshTunnelProxy = new SSHTunnelProxy();
82
95
 
83
- await sshTunnelProxy.execCmd('ls -all', tunnel);
96
+ // connect to remote host
97
+ await sshTunnelProxy.connectSSH(opts);
98
+
99
+ // invoke ls -all on remote host and parse result to json object string
100
+ await lsTest(sshTunnelProxy);
84
101
 
85
- // stream processing complete
86
- console.log('done');
87
102
  process.exit();
88
103
  }
104
+
105
+ runTests();
89
106
  ```
90
107
 
91
108
  The following code is an example of use of the api with electronjs.
package/lib/index.js CHANGED
@@ -40,7 +40,7 @@ class SSHTunnelProxy extends Client {
40
40
  // function to close all active sockets for tunnel restart and exception handling
41
41
  close_sockets(server_name, proxy_ports) {
42
42
 
43
- if (!this.listener[server_name]) return;
43
+ if (!this.listener[server_name] || !proxy_ports) return;
44
44
 
45
45
  proxy_ports.forEach(proxy_port => {
46
46
  const server = this.listener[server_name][proxy_port];
@@ -105,6 +105,11 @@ class SSHTunnelProxy extends Client {
105
105
  setupProxyPorts(server_name, proxy_ports) {
106
106
 
107
107
  return new Promise((resolve, reject) => {
108
+
109
+ if (!proxy_ports) {
110
+ resolve();
111
+ return;
112
+ }
108
113
 
109
114
  var listeners = 0;
110
115
  if (this.listener[server_name]) this.listener[server_name].isConnected = true;
@@ -176,24 +181,19 @@ class SSHTunnelProxy extends Client {
176
181
  stream.on('close', () => {
177
182
  var buf = Buffer.concat(bufs);
178
183
  var err = Buffer.concat(errs);
184
+ //stream.removeListener('exit', onExit);
179
185
  resolve(buf, err);
180
186
  })
181
187
 
182
- if (dataStream) {
183
- stream.pipe(dataStream);
184
- } else {
185
- stream.on('data', (data) => {
186
- bufs.push(data);
187
- })
188
- }
188
+ stream.on('data', (data) => {
189
+ if (dataStream) dataStream.write(data);
190
+ else bufs.push(data);
191
+ })
189
192
 
190
- if (errStream) {
191
- stream.stderr.pipe(errStream);
192
- } else {
193
- stream.stderr.on('data', (data) => {
194
- errs.push(data);
195
- });
196
- }
193
+ stream.stderr.on('data', (data) => {
194
+ if (errStream) errStream.write(data);
195
+ else errs.push(data);
196
+ });
197
197
 
198
198
  }
199
199
  this.exec(cmd, remote_exec);
@@ -259,7 +259,7 @@ class SSHTunnelProxy extends Client {
259
259
  if (opts.shell) {
260
260
  _this.shell(_this.remote_shell);
261
261
 
262
- // if exec requested, exec series of cmds
262
+ // if exec requested, exec series of cmds
263
263
  } else {
264
264
  if (opts.exec && opts.exec.length > 0) {
265
265
  for (var i = 0; i < opts.exec.length; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssh_tunnel_proxy",
3
- "version": "1.2.9",
3
+ "version": "1.2.10",
4
4
  "description": "obtain ngrok host port from api and establish a ssh connection",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -0,0 +1,219 @@
1
+ /*
2
+ test exec cmd on remote server, parse to json object stream
3
+ */
4
+
5
+ //const assert = require('assert');
6
+ //const { describe, it } = require('mocha');
7
+
8
+ const fs = require('fs');
9
+ const process = require('node:process');
10
+ const { SSHTunnelProxy } = require('..');
11
+ const { PassThrough, pipeline } = require("stream");
12
+ const through = require('through');
13
+ const split = require('split');
14
+
15
+ // get config file containing api keys
16
+ const homedir = require('os').homedir();
17
+ var config = null;
18
+ try {
19
+ config = JSON.parse(fs.readFileSync(homedir + '/.config/ssh_tunnel_proxy/config.json'), 'utf8');
20
+ } catch (err) {
21
+ console.log('Error reading config:' + err);
22
+ process.exit();
23
+ }
24
+
25
+ if (!config && !config[1]) {
26
+ console.log('Specified config not found in configs');
27
+ process.exit();
28
+ }
29
+
30
+ const opts = config[1];
31
+
32
+ const sshTunnelProxy = new SSHTunnelProxy();
33
+
34
+ // parse shell command result into array of strings
35
+ function parse_cmd(str) {
36
+
37
+ let result = [];
38
+
39
+ let regex = /(([\w-/_~.\:\[\]]+)|("(.*?)")|('(.*?)'))/g;
40
+ let groups = [2, 4, 6];
41
+ let match;
42
+
43
+ while ((match = regex.exec(str)) !== null) {
44
+ // This is necessary to avoid infinite loops
45
+ // with zero-width matches
46
+ if (match.index === regex.lastIndex) {
47
+ regex.lastIndex++;
48
+ }
49
+
50
+ // For this to work the regex groups need to
51
+ // be mutually exclusive
52
+ groups.forEach(function (group) {
53
+ if (match[group]) {
54
+ result.push(match[group]);
55
+ }
56
+ });
57
+ /*
58
+ let log_matches = false;
59
+ // show matches for debugging
60
+ log_matches && match.forEach(function (m, group) {
61
+ if (m) {
62
+ console.log(`Match '${m}' found in group: ${group}`);
63
+ }
64
+ });
65
+ */
66
+ }
67
+ return result;
68
+ }
69
+
70
+ // generate object from arrays of names/values
71
+ function toObj(names, values) {
72
+ const obj = {};
73
+ for (var i = 0; i < names.length; i++) {
74
+ obj[names[i]] = values[i];
75
+ }
76
+ return obj;
77
+ }
78
+
79
+
80
+ // merge array items beginning at start into single item and append back to array
81
+ function mergeArray(ar, start) {
82
+ const ar2 = ar.slice(start);
83
+ const ar1 = ar.slice(0, start);
84
+ const item = ar2.join(' ');
85
+ ar1.push(item);
86
+ return ar1;
87
+ }
88
+
89
+ /*
90
+ Feb 4 21:34:17 tim-ThinkPad-P50s systemd[1]: Started Run anacron jobs.
91
+ */
92
+ function parseSyslog(line) {
93
+ const format = ['M', 'D', 'T', 'host', 'proc', 'msg'];
94
+ const values = parse_cmd(line);
95
+ const log = mergeArray(values, 5);
96
+ const obj = toObj(format, log);
97
+ if (obj.M && obj.D && obj.T) {
98
+ obj.date = obj.M + ' ' + obj.D + ' ' + obj.T;
99
+ delete obj.M;
100
+ delete obj.D;
101
+ delete obj.T;
102
+ }
103
+ return obj;
104
+ }
105
+
106
+ // parse ls long line output into json object
107
+ function processLSLong(line) {
108
+ const format10 = ['pm', 'links', 'user', 'group', 'size', 'M', 'D', 'H', 'MM', 'name'];
109
+ const format9 = ['pm', 'links', 'user', 'group', 'size', 'M', 'D', 'Y', 'name'];
110
+ const values = parse_cmd(line);
111
+ const obj = toObj((values.length < 10) ? format9 : format10, values);
112
+ if (obj.pm) {
113
+ obj.type = obj.pm.substring(0, 1);
114
+ obj.pm = obj.pm.substring(1);
115
+ }
116
+ if (obj.M && obj.D) {
117
+ obj.date = obj.M + ' ' + obj.D;
118
+ delete obj.M;
119
+ delete obj.D;
120
+ if (obj.Y) {
121
+ obj.date += ' ' + obj.Y;
122
+ delete obj.Y;
123
+ }
124
+ }
125
+ if (obj.H && obj.MM) {
126
+ obj.time = obj.H + ':' + obj.MM;
127
+ delete obj.H;
128
+ delete obj.MM;
129
+ }
130
+ return obj;
131
+ }
132
+
133
+ function to_lsParse() {
134
+ return new through(function (data) {
135
+ const obj = processLSLong(data);
136
+ if (obj.name) this.queue(obj);
137
+ });
138
+ }
139
+
140
+ function to_syslogParse() {
141
+ return new through(function (data) {
142
+ const obj = parseSyslog(data);
143
+ if (obj.proc) this.queue(obj);
144
+ });
145
+ }
146
+
147
+ function to_JSONString() {
148
+ return new through(function (data) {
149
+ this.queue(JSON.stringify(data) + '\n');
150
+ })
151
+ }
152
+
153
+ // lsLongShellProc conversion tests
154
+ async function lsTest(sshTunnelProxy) {
155
+ return new Promise(async (resolve) => {
156
+
157
+ // exec remote command and pipe data through tunnel until end of data
158
+ const tunnel = new PassThrough();
159
+ pipeline(tunnel,
160
+ split(),
161
+ to_lsParse(),
162
+ to_JSONString(),
163
+ process.stdout,
164
+ () => { }
165
+ );
166
+
167
+ const lscmd = 'ls -all';
168
+ //const lscmd = 'ls -all | column --table --table-columns pm,links,user,group,size,month,day,time,name -J';
169
+ console.log('\ninvoking ' + lscmd + ' on remote host:\n');
170
+ await sshTunnelProxy.execCmd(lscmd, tunnel);
171
+
172
+ // stream processing complete
173
+ console.log('ls -all completed');
174
+ resolve();
175
+ })
176
+ }
177
+
178
+ // lsLongShellProc conversion tests
179
+ async function syslogTest(sshTunnelProxy) {
180
+ return new Promise(async (resolve) => {
181
+
182
+ // exec remote command and pipe data through tunnel until end of data
183
+ const tunnel = new PassThrough();
184
+ pipeline(tunnel,
185
+ split(),
186
+ to_syslogParse(),
187
+ to_JSONString(),
188
+ process.stdout,
189
+ () => { }
190
+ );
191
+ //const syslogcmd = 'tail /var/log/syslog | column --table --table-columns-limit 6 --table-columns month,day,time,host,proc,msg -J';
192
+ const syslogcmd = 'tail /var/log/syslog';
193
+ console.log('\ninvoking ' + syslogcmd + ' on remote host:\n');
194
+ await sshTunnelProxy.execCmd(syslogcmd, tunnel);
195
+
196
+ // stream processing complete
197
+ console.log(syslogcmd+' completed');
198
+ resolve();
199
+ })
200
+ }
201
+
202
+ async function runTests() {
203
+
204
+ // connect to remote host
205
+ sshTunnelProxy.debug_en = true;
206
+ await sshTunnelProxy.connectSSH(opts);
207
+
208
+ // invoke ls -all on remote host and parse result to json object string
209
+ await lsTest(sshTunnelProxy);
210
+
211
+ // invoke tail /var/log/syslog on remote host and parse result to json object string
212
+ await syslogTest(sshTunnelProxy);
213
+
214
+ process.exit();
215
+ }
216
+
217
+ runTests();
218
+
219
+
@@ -1,140 +0,0 @@
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();