From 0bfb7402532c5ad687434590e646388b297b41aa Mon Sep 17 00:00:00 2001 From: Sam Eaton Date: Fri, 8 Apr 2016 14:28:58 -0600 Subject: [PATCH] adds auto port generation --- README.md | 41 ++++++++++++++++++++++++++++-------- demo.js | 59 +++++++++++++++++++++++++++++++++++++++++----------- index.js | 41 ++++++++++++++++++++++++++---------- src/index.js | 35 ++++++++++++++++++------------- 4 files changed, 130 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index c0aa28f..e7bd7e8 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ autossh({ .on('error', err => { console.error('ERROR: ', err); }) -.on('init', () => { - console.log('connected. Ready to do other stuff.'); +.on('connect', connection => { + console.log('connected. Tunnel established on port ' + connection.localPort); + console.log('pid: ' + connection.pid); }); ``` @@ -37,11 +38,21 @@ autossh({ ssh -NL 64444:localhost:5432 root@111.22.333.444 ``` -#### To Kill +#### Generate Dynamic Local Port + +If you want to dynamically/randomly generate a port number, provide a string `auto` for the `localPort`. + +Port conflicts will automatically be avoided and the generated port will not be in use. + +See `demo.js` for ean example. + +#### Killing the Autossh Process The autossh process will automatically die if the node process is closed, but you can manually kill the process using `kill`. -**Example** +If you try to kill the ssh process from the command line while the node process is active, a new ssh tunnel will be established (which is the point of autossh). You will need to kill the node process first or call the `kill` method on the instance. + +**Example 1** ``` javascript const myAutossh = autossh({ @@ -50,12 +61,24 @@ const myAutossh = autossh({ localPort: 64444, remotePort: 5432 }) -.on('error', err => { - console.error('ERROR: ', err); -}) -.on('init', () => { - console.log('connected. Ready to do other stuff.'); +.on('connect', connection => { + console.log('connected: ', connection); }); myAutossh.kill(); ``` + +**Example 2** + +``` javascript +const myAutossh = autossh({ + host: '111.22.333.444', + username: 'root', + localPort: 64444, + remotePort: 5432 +}) +.on('connect', connection => { + console.log('connected: ', connection); + connection.kill(); +}); +``` diff --git a/demo.js b/demo.js index 091ed4e..a59729b 100644 --- a/demo.js +++ b/demo.js @@ -1,17 +1,52 @@ 'use strict'; const autossh = require('./index.js'); +// open 10 ssh tunnels +for (let i = 0; i < 5; i++) { + // set up config + autossh({ + host: '104.131.150.215', // enter host address + username: 'same', // enter username + localPort: 'auto', // 'auto' or port number + remotePort: 5432 + }) + // listen for errors + .on('error', err => { + console.error('ERROR: ', err); + }) + // listen for connection + .on('connect', connection => { + console.log('\n**connected**'); + console.log('localPort: \t' + connection.localPort); + console.log('pid: \t\t' + connection.pid); + console.log(`tunnel established from localhost:${connection.localPort} to ${connection.host}:${connection.remotePort} as ${connection.username}`); + }); +} -autossh({ - host: '104.131.150.215', - username: 'same', - localPort: 60001, - remotePort: 5432 -}) -.on('error', err => { - console.error('ERROR: ', err); -}) -.on('init', connection => { - console.log('connected.'); -}); +/* Possible output example (localPort results are be random) +**connected** +localPort: 39570 +pid: 7477 +tunnel established from localhost:39570 to xyz.xy.xyz.yz:5432 as same + +**connected** +localPort: 53139 +pid: 7478 +tunnel established from localhost:53139 to xyz.xy.xyz.yz:5432 as same + +**connected** +localPort: 56421 +pid: 7479 +tunnel established from localhost:56421 to xyz.xy.xyz.yz:5432 as same + +**connected** +localPort: 5360 +pid: 7480 +tunnel established from localhost:5360 to xyz.xy.xyz.yz:5432 as same + +**connected** +localPort: 40321 +pid: 7481 +tunnel established from localhost:40321 to xyz.xy.xyz.yz:5432 as same +*/ diff --git a/index.js b/index.js index 2086e8a..3941d56 100644 --- a/index.js +++ b/index.js @@ -36,19 +36,31 @@ var AutoSSH = function (_EventEmitter) { _this.remotePort = conf.remotePort; _this.localPort = conf.localPort; - _portfinder2.default.basePort = _this.localPort; - _portfinder2.default.getPort({ port: _this.localPort }, function (err, result) { - console.log('Error: ', err); - console.log('result: ', result); - }); setImmediate(function () { - if (!conf.host) return _this.emit('error', 'Missing host'); - if (!conf.username) return _this.emit('error', 'Missing username'); - if (!conf.remotePort) return _this.emit('error', 'Missing remotePort'); if (!conf.localPort) return _this.emit('error', 'Missing localPort'); - _this.execTunnel(); - _this.emit('init', { kill: _this.kill, pid: _this.currentProcess.pid, currentProcess: _this.currentProcess }); + _portfinder2.default.getPort({ + port: _this.localPort === 'auto' ? _this.generateRandomPort() : _this.localPort + }, function (err, freePort) { + if (err) return _this.emit('error', 'Port error: ' + err); + if (_this.localPort !== 'auto' && _this.localPort !== freePort) return _this.emit('error', 'Port ' + _this.localPort + ' is not available'); + if (!conf.host) return _this.emit('error', 'Missing host'); + if (!conf.username) return _this.emit('error', 'Missing username'); + if (!conf.remotePort) return _this.emit('error', 'Missing remotePort'); + + _this.localPort = freePort; + + _this.execTunnel(); + + _this.emit('connect', { + kill: _this.kill, + pid: _this.currentProcess.pid, + host: _this.host, + username: _this.username, + remotePort: _this.remotePort, + localPort: _this.localPort + }); + }); }); process.on('exit', function () { @@ -58,6 +70,13 @@ var AutoSSH = function (_EventEmitter) { } _createClass(AutoSSH, [{ + key: 'generateRandomPort', + value: function generateRandomPort() { + var minPort = 3000; + var maxPort = 65535; + return Math.floor(Math.random() * (maxPort - minPort + 1)) + minPort; + } + }, { key: 'execTunnel', value: function execTunnel() { var _this2 = this; @@ -66,7 +85,7 @@ var AutoSSH = function (_EventEmitter) { var userAtHost = this.username + '@' + this.host; var exitOnFailure = '-o "ExitOnForwardFailure yes"'; var execString = 'ssh -NL ' + bindAddress + ' ' + exitOnFailure + ' ' + userAtHost; - console.log('execString: ', execString); + this.currentProcess = (0, _child_process.exec)(execString, function (err, stdout, stderr) { if (/Address already in use/i.test(stderr)) { _this2.kill(); diff --git a/src/index.js b/src/index.js index 90395f3..8325b54 100644 --- a/src/index.js +++ b/src/index.js @@ -13,48 +13,55 @@ class AutoSSH extends EventEmitter { this.remotePort = conf.remotePort; this.localPort = conf.localPort; - portfinder.getPort({port: this.localPort === 'auto' ? 8000 : this.localPort}, (err, freePort) => { - console.log('Error: ', err); - if (this.localPort !== 'auto' && this.localPort !== freePort) { - return console.error(`Port ${this.localPort} is not available`); - } - this.localPort = result; - setImmediate(() => { + setImmediate(() => { + if (!conf.localPort) + return this.emit('error', 'Missing localPort'); + + portfinder.getPort({ + port: this.localPort === 'auto' ? this.generateRandomPort() : this.localPort + }, (err, freePort) => { + if (err) + return this.emit('error', 'Port error: ' + err); + if (this.localPort !== 'auto' && this.localPort !== freePort) + return this.emit('error', `Port ${this.localPort} is not available`); if (!conf.host) return this.emit('error', 'Missing host'); if (!conf.username) return this.emit('error', 'Missing username'); if (!conf.remotePort) return this.emit('error', 'Missing remotePort'); - if (!conf.localPort) - return this.emit('error', 'Missing localPort'); + + this.localPort = freePort; this.execTunnel(); - this.emit('init', { + this.emit('connect', { kill: this.kill, pid: this.currentProcess.pid, host: this.host, username: this.username, remotePort: this.remotePort, - localPort: this.localPort, + localPort: this.localPort }); }); }); - process.on('exit', () => { this.kill(); }); } - + generateRandomPort() { + const minPort = 3000; + const maxPort = 65535; + return Math.floor(Math.random() * (maxPort - minPort + 1)) + minPort; + } execTunnel() { const bindAddress = `${this.localPort}:localhost:${this.remotePort}`; const userAtHost = `${this.username}@${this.host}`; const exitOnFailure = '-o "ExitOnForwardFailure yes"' const execString = `ssh -NL ${bindAddress} ${exitOnFailure} ${userAtHost}`; - console.log('execString: ', execString); + this.currentProcess = exec(execString, (err, stdout, stderr) => { if (/Address already in use/i.test(stderr)) { this.kill();