From 91b27e11bd9f0fc6797200e3560275bdcf476531 Mon Sep 17 00:00:00 2001 From: Sam Eaton Date: Thu, 7 Apr 2016 19:22:13 -0600 Subject: [PATCH] initial commit --- .babelrc | 3 ++ .gitignore | 2 ++ demo.js | 17 +++++++++++ index.js | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 28 ++++++++++++++++++ src/index.js | 60 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 191 insertions(+) create mode 100644 .babelrc create mode 100644 .gitignore create mode 100644 demo.js create mode 100644 index.js create mode 100644 package.json create mode 100644 src/index.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..c13c5f6 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015"] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb03e3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +*.log diff --git a/demo.js b/demo.js new file mode 100644 index 0000000..cae3f28 --- /dev/null +++ b/demo.js @@ -0,0 +1,17 @@ +'use strict'; +const autossh = require('./index.js'); + + +autossh({ + host: '104.131.150.215', + username: 'same', + localPort: 60001, + remotePort: 5432 +}) +.on('error', err => { + console.error('ERROR: ', err); +}) +.on('init', () => { + console.log('connected.'); +}); + diff --git a/index.js b/index.js new file mode 100644 index 0000000..19dbfd3 --- /dev/null +++ b/index.js @@ -0,0 +1,81 @@ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _child_process = require('child_process'); + +var _events = require('events'); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var SshTunnel = function (_EventEmitter) { + _inherits(SshTunnel, _EventEmitter); + + function SshTunnel() { + var conf = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + var cb = arguments[1]; + + _classCallCheck(this, SshTunnel); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(SshTunnel).call(this)); + + _this.host = conf.host; + _this.username = conf.username; + _this.remotePort = conf.remotePort; + _this.localPort = conf.localPort; + + 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'); + process.on('exit', function () { + _this.kill(); + }); + }); + return _this; + } + + _createClass(SshTunnel, [{ + key: 'execTunnel', + value: function execTunnel() { + var _this2 = this; + + var host = this.host; + var username = this.username; + var localPort = this.localPort; + var remotePort = this.remotePort; + this.currentProcess = (0, _child_process.exec)('ssh -NL ' + localPort + ':localhost:' + remotePort + ' ' + username + '@' + host, function (err, stdout, stderr) { + console.log('\nerr:', err); + console.log('\nstdout:', stdout); + console.log('\nstderr:', stderr); + + if (err) _this2.emit('error', err); + + if (!_this2.killed) _this2.execTunnel(); + }); + } + }, { + key: 'kill', + value: function kill() { + this.killed = true; + this.currentProcess.kill(); + return this; + } + }]); + + return SshTunnel; +}(_events.EventEmitter); + +function autoSSH(conf, cb) { + return new SshTunnel(conf, cb); +} + +module.exports = autoSSH; diff --git a/package.json b/package.json new file mode 100644 index 0000000..2300e4d --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "autossh", + "version": "0.0.1", + "description": "Persistent SSH tunnels", + "main": "index.js", + "scripts": { + "transpile": "babel src -d ." + }, + "repository": { + "type": "git", + "url": "git+https://github.com/samueleaton/autossh.git" + }, + "keywords": [ + "ssh", + "tunnel", + "autossh" + ], + "author": "Sam Eaton", + "license": "ISC", + "bugs": { + "url": "https://github.com/samueleaton/autossh/issues" + }, + "homepage": "https://github.com/samueleaton/autossh#readme", + "devDependencies": { + "babel-cli": "^6.6.5", + "babel-preset-es2015": "^6.6.0" + } +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..9997b16 --- /dev/null +++ b/src/index.js @@ -0,0 +1,60 @@ +import { exec } from 'child_process'; +import { EventEmitter } from 'events'; + +class SshTunnel extends EventEmitter { + constructor(conf = {}, cb) { + super(); + this.host = conf.host; + this.username = conf.username; + this.remotePort = conf.remotePort; + this.localPort = conf.localPort; + + setImmediate(() => { + 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'); + process.on('exit', () => { + this.kill(); + }); + }); + } + + execTunnel() { + const host = this.host; + const username = this.username; + const localPort = this.localPort; + const remotePort = this.remotePort; + this.currentProcess = exec(`ssh -NL ${localPort}:localhost:${remotePort} ${username}@${host}`, + (err, stdout, stderr) => { + console.log('\nerr:', err); + console.log('\nstdout:', stdout); + console.log('\nstderr:', stderr); + + if (err) this.emit('error', err); + + if (!this.killed) + this.execTunnel(); + } + ); + } + + kill() { + this.killed = true; + this.currentProcess.kill(); + return this; + } +} + +function autoSSH(conf, cb) { + return new SshTunnel(conf, cb); +} + +module.exports = autoSSH;