var util = require('util') var events = require('events') var net = require('net') var ForwardReader = require('./reader') var ForwardWriter = require('./writer') // Handles multiple ports function ForwardManager() { var handlersById = Object.create(null) this.has = function(id) { return !!handlersById[id] } this.add = function(id, conn, options) { function endListener() { delete handlersById[id] this.emit('remove', id, options) } if (this.has(id)) { this.remove(id) } var handler = new ForwardHandler(conn, options) handler.on('end', endListener.bind(this)) handlersById[id] = handler this.emit('add', id, options) } this.remove = function(id) { var handler = handlersById[id] if (handler) { handler.end() } } this.removeAll = function() { Object.keys(handlersById).forEach(function(id) { handlersById[id].end() }) } this.listAll = function() { return Object.keys(handlersById).map(function(id) { var handler = handlersById[id] return { id: id , devicePort: handler.options.devicePort , targetHost: handler.options.targetHost , targetPort: handler.options.targetPort } }) } events.EventEmitter.call(this) } util.inherits(ForwardManager, events.EventEmitter) // Handles a single port function ForwardHandler(conn, options) { var destHandlersById = Object.create(null) function endListener() { this.emit('end') } function packetEndListener(id) { delete destHandlersById[id] } function packetListener(id, packet) { var dest = destHandlersById[id] if (packet) { if (!dest) { // Let's create a new connection dest = destHandlersById[id] = new DestHandler(id, conn, options) dest.on('end', packetEndListener.bind(null, id)) } dest.write(packet) } else { // It's a simulated fin packet if (dest) { dest.end() } } } function readableListener() { // No-op but must exist so that we get the 'end' event. } conn.pipe(new ForwardReader()) .on('end', endListener.bind(this)) .on('packet', packetListener) .on('readable', readableListener) this.options = options this.end = function() { conn.end() } events.EventEmitter.call(this) } util.inherits(ForwardHandler, events.EventEmitter) // Handles a single connection function DestHandler(id, conn, options) { function endListener() { conn.removeListener('drain', drainListener) this.emit('end') } function errorListener() { writer.end() } function readableListener() { maybePipeManually() } function drainListener() { maybePipeManually() } // We can't just pipe to conn because we don't want to end it // when the dest closes. Instead we'll send a special packet // to it (which is handled by the writer). function maybePipeManually() { var chunk while ((chunk = writer.read())) { if (!conn.write(chunk)) { break } } } var dest = net.connect({ host: options.targetHost , port: options.targetPort }) .on('error', errorListener) var writer = dest.pipe(new ForwardWriter(id)) .on('end', endListener.bind(this)) .on('readable', readableListener) conn.on('drain', drainListener) this.end = function() { dest.end() } this.write = function(chunk) { dest.write(chunk) } events.EventEmitter.call(this) } util.inherits(DestHandler, events.EventEmitter) module.exports = ForwardManager