diff --git a/README.md b/README.md
index 07933cb..005eec7 100644
--- a/README.md
+++ b/README.md
@@ -224,6 +224,30 @@ autossh({
+#### Setting up a Reverse (Remote) Tunnel
+
+To set up a reverse tunnel set `reverse` to `true` in the config object.
+
+```javascript
+autossh({
+ host: '111.22.333.444',
+ username: 'root',
+ localPort: 22,
+ remotePort: 5432,
+ reverse: true
+})
+.on('error', err => {
+ console.error('ERROR: ', err);
+})
+.on('connect', connection => {
+ console.log('connection pid: ' + connection.pid);
+});
+```
+
+When using the reverse tunnel option, the `localPort` value cannot be `'auto'`.
+
+
+
#### Adjusting/Disabling Max Poll Count
When first trying to establish the ssh tunnel, `autoshh` will poll the local port until the connection has been established. The default max poll count is `30`.
diff --git a/index.js b/index.js
index 6a20de2..7226aba 100644
--- a/index.js
+++ b/index.js
@@ -36,21 +36,7 @@ var AutoSSH = function (_EventEmitter) {
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(AutoSSH).call(this));
- _this.host = conf.host;
- _this.username = conf.username || 'root';
- _this.remotePort = conf.remotePort;
- _this.localPort = conf.localPort || 'auto';
-
- _this.pollCount = 0;
- _this.maxPollCount = conf.maxPollCount || 30;
- _this.pollTimeout = 75;
-
- _this.serverAliveInterval = typeof conf.serverAliveInterval === 'number' ? conf.serverAliveInterval : 120;
-
- _this.serverAliveCountMax = typeof conf.serverAliveCountMax === 'number' ? conf.serverAliveCountMax : 1;
-
- _this.sshPort = conf.sshPort || 22;
- _this.privateKey = conf.privateKey || null;
+ _this.configure(conf);
setImmediate(function () {
var confErrors = _this.getConfErrors(conf);
@@ -68,28 +54,55 @@ var AutoSSH = function (_EventEmitter) {
return _this;
}
- /*
- */
-
-
_createClass(AutoSSH, [{
+ key: 'configure',
+ value: function configure(conf) {
+ this.reverse = conf.reverse === true || false;
+
+ this.host = conf.host;
+ this.username = conf.username || 'root';
+ this.remotePort = conf.remotePort;
+
+ if (this.reverse) this.localPort = parseInt(conf.localPort) || 22;else this.localPort = conf.localPort || 'auto';
+
+ this.pollCount = 0;
+ this.maxPollCount = conf.maxPollCount || 30;
+ this.pollTimeout = 75;
+
+ this.serverAliveInterval = typeof conf.serverAliveInterval === 'number' ? conf.serverAliveInterval : 120;
+
+ this.serverAliveCountMax = typeof conf.serverAliveCountMax === 'number' ? conf.serverAliveCountMax : 1;
+
+ this.sshPort = conf.sshPort || 22;
+ this.privateKey = conf.privateKey || null;
+ }
+
+ /*
+ */
+
+ }, {
key: 'connect',
value: function connect(conf) {
var _this2 = this;
var port = this.localPort === 'auto' ? this.generateRandomPort() : this.localPort;
-
- _portfinder2.default.getPort({ port: port }, function (portfinderErr, freePort) {
- if (_this2.killed) return;
- if (portfinderErr) _this2.emit('error', 'Port error: ' + portfinderErr);
- if (_this2.localPort !== 'auto' && _this2.localPort !== freePort) _this2.emit('error', 'Port ' + _this2.localPort + ' is not available');else {
- _this2.localPort = freePort;
- // creates tunnel and then polls port until connection is established
- _this2.execTunnel(function () {
- _this2.pollConnection();
- });
- }
- });
+ if (this.reverse) {
+ this.execTunnel(function () {
+ _this2.pollConnection();
+ });
+ } else {
+ _portfinder2.default.getPort({ port: port }, function (portfinderErr, freePort) {
+ if (_this2.killed) return;
+ if (portfinderErr) _this2.emit('error', 'Port error: ' + portfinderErr);
+ if (_this2.localPort !== 'auto' && _this2.localPort !== freePort) _this2.emit('error', 'Port ' + _this2.localPort + ' is not available');else {
+ _this2.localPort = freePort;
+ // creates tunnel and then polls port until connection is established
+ _this2.execTunnel(function () {
+ _this2.pollConnection();
+ });
+ }
+ });
+ }
}
/*
@@ -196,7 +209,8 @@ var AutoSSH = function (_EventEmitter) {
key: 'getConfErrors',
value: function getConfErrors(conf) {
var errors = [];
- if (!conf.localPort) errors.push('Missing localPort');else if (isNaN(parseInt(conf.localPort)) && conf.localPort !== 'auto') errors.push('Invalid localPort');
+ if (!conf.localPort) errors.push('Missing localPort');
+ if (conf.reverse === true && (conf.localPort === 'auto' || isNaN(parseInt(conf.localPort)))) errors.push('Invalid value for localPort');else if (isNaN(parseInt(conf.localPort)) && conf.localPort !== 'auto') errors.push('Invalid value for localPort');
if (!conf.host) errors.push('Missing host');else if (typeof conf.host !== 'string') {
errors.push('host must be type "string". was given "' + _typeof(conf.host) + '"');
@@ -259,7 +273,7 @@ var AutoSSH = function (_EventEmitter) {
var serverAliveOpts = this.generateServerAliveOptions();
var defaultOpts = this.generateDefaultOptions();
var privateKey = this.privateKey ? '-i ' + this.privateKey : '';
- var sshPort = this.sshPort ? '-p ' + this.sshPort : '';
+ var sshPort = this.sshPort === 22 ? '' : '-p ' + this.sshPort;
return defaultOpts + ' ' + serverAliveOpts + ' ' + privateKey + ' ' + sshPort;
}
@@ -270,11 +284,14 @@ var AutoSSH = function (_EventEmitter) {
}, {
key: 'generateExecString',
value: function generateExecString() {
- var bindAddress = this.localPort + ':localhost:' + this.remotePort;
+ var startPort = this.reverse ? this.remotePort : this.localPort;
+ var endPort = this.reverse ? this.localPort : this.remotePort;
+ var bindAddress = startPort + ':localhost:' + endPort;
var options = this.generateExecOptions();
var userAtHost = this.username + '@' + this.host;
+ var method = this.reverse ? 'R' : 'L';
- return 'ssh -NL ' + bindAddress + ' ' + options + ' ' + userAtHost;
+ return 'ssh -N' + method + ' ' + bindAddress + ' ' + options + ' ' + userAtHost;
}
/*
diff --git a/src/index.js b/src/index.js
index 7a949f8..b97f6e8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -10,23 +10,7 @@ class AutoSSH extends EventEmitter {
constructor(conf = {}) {
super();
- this.host = conf.host;
- this.username = conf.username || 'root';
- this.remotePort = conf.remotePort;
- this.localPort = conf.localPort || 'auto';
-
- this.pollCount = 0;
- this.maxPollCount = conf.maxPollCount || 30;
- this.pollTimeout = 75;
-
- this.serverAliveInterval = typeof conf.serverAliveInterval === 'number' ?
- conf.serverAliveInterval : 120;
-
- this.serverAliveCountMax = typeof conf.serverAliveCountMax === 'number' ?
- conf.serverAliveCountMax : 1;
-
- this.sshPort = conf.sshPort || 22;
- this.privateKey = conf.privateKey || null;
+ this.configure(conf);
setImmediate(() => {
const confErrors = this.getConfErrors(conf);
@@ -42,26 +26,58 @@ class AutoSSH extends EventEmitter {
});
}
+ configure(conf) {
+ this.reverse = conf.reverse === true || false;
+
+ this.host = conf.host;
+ this.username = conf.username || 'root';
+ this.remotePort = conf.remotePort;
+
+ if (this.reverse)
+ this.localPort = parseInt(conf.localPort) || 22;
+ else
+ this.localPort = conf.localPort || 'auto';
+
+ this.pollCount = 0;
+ this.maxPollCount = conf.maxPollCount || 30;
+ this.pollTimeout = 75;
+
+ this.serverAliveInterval = typeof conf.serverAliveInterval === 'number' ?
+ conf.serverAliveInterval : 120;
+
+ this.serverAliveCountMax = typeof conf.serverAliveCountMax === 'number' ?
+ conf.serverAliveCountMax : 1;
+
+ this.sshPort = conf.sshPort || 22;
+ this.privateKey = conf.privateKey || null;
+ }
+
/*
*/
connect(conf) {
const port = this.localPort === 'auto' ? this.generateRandomPort() : this.localPort;
-
- portfinder.getPort({ port }, (portfinderErr, freePort) => {
- if (this.killed)
- return;
- if (portfinderErr)
- this.emit('error', 'Port error: ' + portfinderErr);
- if (this.localPort !== 'auto' && this.localPort !== freePort)
- this.emit('error', `Port ${this.localPort} is not available`);
- else {
- this.localPort = freePort;
- // creates tunnel and then polls port until connection is established
- this.execTunnel(() => {
- this.pollConnection();
- });
- }
- });
+ if (this.reverse) {
+ this.execTunnel(() => {
+ this.pollConnection();
+ });
+ }
+ else {
+ portfinder.getPort({ port }, (portfinderErr, freePort) => {
+ if (this.killed)
+ return;
+ if (portfinderErr)
+ this.emit('error', 'Port error: ' + portfinderErr);
+ if (this.localPort !== 'auto' && this.localPort !== freePort)
+ this.emit('error', `Port ${this.localPort} is not available`);
+ else {
+ this.localPort = freePort;
+ // creates tunnel and then polls port until connection is established
+ this.execTunnel(() => {
+ this.pollConnection();
+ });
+ }
+ });
+ }
}
/*
@@ -151,8 +167,10 @@ class AutoSSH extends EventEmitter {
const errors = [];
if (!conf.localPort)
errors.push('Missing localPort');
+ if (conf.reverse === true && (conf.localPort === 'auto' || isNaN(parseInt(conf.localPort))))
+ errors.push('Invalid value for localPort');
else if (isNaN(parseInt(conf.localPort)) && conf.localPort !== 'auto')
- errors.push('Invalid localPort');
+ errors.push('Invalid value for localPort');
if (!conf.host)
errors.push('Missing host');
@@ -217,7 +235,7 @@ class AutoSSH extends EventEmitter {
const serverAliveOpts = this.generateServerAliveOptions();
const defaultOpts = this.generateDefaultOptions();
const privateKey = this.privateKey ? `-i ${this.privateKey}` : '';
- const sshPort = this.sshPort ? `-p ${this.sshPort}` : '';
+ const sshPort = this.sshPort === 22 ? '' : `-p ${this.sshPort}`;
return `${defaultOpts} ${serverAliveOpts} ${privateKey} ${sshPort}`;
}
@@ -225,11 +243,14 @@ class AutoSSH extends EventEmitter {
/*
*/
generateExecString() {
- const bindAddress = `${this.localPort}:localhost:${this.remotePort}`;
+ const startPort = this.reverse ? this.remotePort : this.localPort;
+ const endPort = this.reverse ? this.localPort : this.remotePort;
+ const bindAddress = `${startPort}:localhost:${endPort}`;
const options = this.generateExecOptions();
const userAtHost = `${this.username}@${this.host}`;
+ const method = this.reverse ? 'R' : 'L';
- return `ssh -NL ${bindAddress} ${options} ${userAtHost}`;
+ return `ssh -N${method} ${bindAddress} ${options} ${userAtHost}`;
}
/*