UNPKG

tunnel-ssh

Version:
136 lines (111 loc) 4.31 kB
const net = require('net'); const { Client } = require('ssh2'); const os = require('os'); function autoClose(server, connection) { connection.on('close', () => { server.getConnections((error, count) => { if (count === 0) { server.close(); } }); }); } async function createServer(options) { let serverOptions = Object.assign({}, options); if (!serverOptions.port && !serverOptions.path) { serverOptions = null; } return new Promise((resolve, reject) => { let server = net.createServer(); let errorHandler = function (error) { reject(error); }; server.on('error', errorHandler); process.on('uncaughtException', errorHandler); server.listen(serverOptions); server.on('listening', () => { process.removeListener('uncaughtException', errorHandler); resolve(server); }); }); } async function createSSHConnection(config) { return new Promise(function (resolve, reject) { let conn = new Client(); conn.on('ready', () => resolve(conn)); conn.on('error', reject); conn.connect(config); }); } async function createTunnel( tunnelOptions, serverOptions, sshOptions, forwardOptions ) { let sshOptionslocal = Object.assign({ port: 22, username: 'root' }, sshOptions); let forwardOptionsLocal = Object.assign({ dstAddr: '0.0.0.0' }, forwardOptions); let tunnelOptionsLocal = Object.assign({ autoClose: false, reconnectOnError: false }, tunnelOptions || {}); let server, sshConnection; return new Promise(async function (resolve, reject) { try { sshConnection = await createSSHConnection(sshOptionslocal); addListenerSshConnection(sshConnection); } catch (e) { if (server) { server.close() } return reject(e); } try { server = await createServer(serverOptions); addListenerServer(server); } catch (e) { return reject(e); } function addListenerSshConnection(sshConnection) { if (tunnelOptionsLocal.reconnectOnError) { sshConnection.on('error', async () => { sshConnection.isBroken = true; sshConnection = await createSSHConnection(sshOptionslocal); addListenerSshConnection(sshConnection); }); } } function addListenerServer(server) { if (tunnelOptionsLocal.reconnectOnError) { server.on('error', async () => { server = await createServer(serverOptions); addListenerServer(server); }); } server.on('connection', onConnectionHandler); server.on('close', () => sshConnection.end()); } function onConnectionHandler(clientConnection) { if (!forwardOptionsLocal.srcPort) { forwardOptionsLocal.srcPort = server.address().port; } if (!forwardOptionsLocal.srcAddr) { forwardOptionsLocal.srcAddr = server.address().address; } if (tunnelOptionsLocal.autoClose) { autoClose(server, clientConnection); } if (sshConnection.isBroken) { return; } sshConnection.forwardOut( forwardOptionsLocal.srcAddr, forwardOptionsLocal.srcPort, forwardOptionsLocal.dstAddr, forwardOptionsLocal.dstPort, (err, stream) => { if (err) { if (server) { server.close() } sshConnection.emit("error", err); } else { clientConnection.pipe(stream).pipe(clientConnection); } }); } resolve([server, sshConnection]); }); } exports.createTunnel = createTunnel;