rewrite.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. const events = require('events');
  2. const sshClient = require('ssh2');
  3. const net = require('net');
  4. const debug = require('debug')('tunnel-ssh');
  5. const createConfig = require('./lib/config');
  6. class Tunnel extends events.EventEmitter {
  7. constructor(config) {
  8. super();
  9. this.config = config;
  10. // Expose sshClient for external event-bindings
  11. // @TODO exclude into separate util function
  12. this.sshClient = sshClient();
  13. this.sshClient.promise = new Promise((resolve, reject) => {
  14. this.sshClient
  15. .on('ready', () => resolve(this.sshClient))
  16. .on('error', error => reject(error))
  17. .connect(config);
  18. });
  19. }
  20. /**
  21. * Creates a dublex stream
  22. * @returns {Promise.<Stream>}
  23. */
  24. getStream(srcHost, srcPort, dstHost, dstPort) {
  25. // @todo implement old behavior "create a new client for every connection"
  26. return this.sshClient.promise.then(client => {
  27. return new Promise((resolve, reject) => {
  28. return client.forwardOut(
  29. srcHost,
  30. srcPort,
  31. dstHost,
  32. dstPort, (error, sshConnection) => {
  33. if (error) {
  34. this.emit('error', error);
  35. return reject(error);
  36. }
  37. return resolve(sshConnection);
  38. });
  39. });
  40. });
  41. }
  42. /**
  43. * Creates a tcp server as entry point for the ssh tunnel,
  44. * every incoming tcp connection is piped to the tunnel.
  45. * @returns {Promise.<net.Server>}
  46. */
  47. listen(port, addr, srcHost, srcPort, dstHost, dstPort) {
  48. let server = net.createServer();
  49. server.promise = new Promise((resolve, reject) => {
  50. server.on('listening', () => resolve(server));
  51. server.on('error', error => reject(error));
  52. });
  53. server.promise.catch(e => {
  54. this.emit('error', e);
  55. });
  56. server.on('connection', tcpConnection => {
  57. this.getStream(srcHost, srcPort, dstHost, dstPort).then(sshConnection => {
  58. debug('sshConnection:create');
  59. this.emit('sshConnection', sshConnection);
  60. if (this.config.exitOnLastConnectionEnd === true) {
  61. tcpConnection.on('close', () => server.getConnections((error, count) => {
  62. if (error) {
  63. this.emit('error', error);
  64. }
  65. debug('ConnectionCount => ' + count);
  66. if (count === 0) {
  67. server.close();
  68. }
  69. }));
  70. }
  71. tcpConnection.pipe(sshConnection).pipe(tcpConnection);
  72. sshConnection.on('end', () => console.log('ssh-connection end'));
  73. sshConnection.on('close', () => console.log('ssh-connection close'));
  74. }).catch(error => this.emit('error', error));
  75. });
  76. server.on('close', () => {
  77. debug('server::close');
  78. this.sshClient.end();
  79. });
  80. return this.sshClient.promise.then(() => {
  81. server.listen(port, addr);
  82. return server;
  83. }).catch(error => this.emit('error', error));
  84. }
  85. }
  86. function tunnel(rawConfig) {
  87. let config = createConfig(rawConfig);
  88. let tnl = new Tunnel(config);
  89. tnl.listen(
  90. config.bindPort,
  91. config.bindAddr,
  92. config.srcHost,
  93. config.srcPort,
  94. config.dstHost,
  95. config.dstPort
  96. ).then(server => {
  97. server.on('listening', function () {
  98. console.log('listening');
  99. });
  100. }).catch(error => this.emit('error', error));
  101. Promise.all(config.ports.map(port => {
  102. return tnl.listen(
  103. port,
  104. config.bindAddr,
  105. config.srcHost,
  106. config.srcPort,
  107. config.dstHost,
  108. port
  109. );
  110. })).then(() => console.log('done')).catch(error => console.log(error))
  111. return tnl;
  112. }
  113. /*
  114. var http = require('http');
  115. var s = http.createServer(function (req, res) {
  116. res.setHeader('Content-Type', 'text/html');
  117. res.setHeader('X-Foo', 'bar');
  118. res.writeHead(200, {'Content-Type': 'text/plain'});
  119. res.write('foo');
  120. res.end('ok');
  121. });
  122. s.listen(7000);
  123. s.unref();
  124. */
  125. tunnel({
  126. host: 'pi', user: 'pi', passphrase: '*gold12', dstPort: 8080, bindPort: 8082, ports: [8081]
  127. }).on('error', error => console.log(error));