diff --git a/lib/sorting-hat.js b/lib/sorting-hat.js index 04e3770..a105864 100644 --- a/lib/sorting-hat.js +++ b/lib/sorting-hat.js @@ -78,6 +78,7 @@ module.exports.assign = function (state, tun, cb) { state.httpRedirectServer.emit('connection', socket); }; handlers.https = function (tlsSocket) { + console.log('Enccrypted', tlsSocket.encrypted, tlsSocket.remoteAddress, tlsSocket.remotePort); if (!state.defaultHttpServer) { state.defaultHttpServer = require('http').createServer(function (req, res) { console.log('[hit http/s server]'); @@ -87,6 +88,27 @@ module.exports.assign = function (state, tun, cb) { state.defaultHttpServer.emit('connection', tlsSocket); }; + function getNetConn(port) { + var netOpts = { + port: port + , host: '127.0.0.1' + + , servername: tun.name + , name: tun.name + , serviceport: tun.serviceport + , data: tun.data + , remoteFamily: tun.family + , remoteAddress: tun.address + , remotePort: tun.port + }; + var conn = net.createConnection(netOpts, function () { + // this will happen before 'data' or 'readable' is triggered + // We use the data from the netOpts object so that the createConnection function has + // the oppurtunity of removing/changing it if it wants/needs to handle it differently. + }); + return conn; + } + if ('http' === tun.service || 'https' === tun.service) { if (!tun.name) { cb(new Error("No routing information for ':tun_id'. Service '" + tun.service + "' is missing 'name'.")); @@ -96,7 +118,7 @@ module.exports.assign = function (state, tun, cb) { function redirectHttp(cb) { var socketPair = require('socket-pair'); - conn = socketPair.create(function (err, other) { + var conn = socketPair.create(function (err, other) { if (err) { cb(err); return; } handlers.http(other); cb(null, conn); @@ -130,23 +152,80 @@ module.exports.assign = function (state, tun, cb) { return; } - function terminateTls(cb) { + function defineProps(other, tun) { + Object.defineProperty(other, 'remoteFamily', { + enumerable: false, + configurable: true, + get: function() { + return tun.family; + } + }); + Object.defineProperty(other, 'remoteAddress', { + enumerable: false, + configurable: true, + get: function() { + return tun.address; + } + }); + Object.defineProperty(other, 'remotePort', { + enumerable: false, + configurable: true, + get: function() { + return parseInt(tun.port); + } + }); + Object.defineProperty(other, 'localPort', { + enumerable: false, + configurable: true, + get: function() { + return parseInt(tun.serviceport); + } + }); + } + + function terminateTls(tun, cb) { var socketPair = require('socket-pair'); - conn = socketPair.create(function (err, other) { + var conn = socketPair.create(function (err, other) { if (err) { cb(err); return; } + //console.log('[hit tcp connection]', other.remoteFamily, other.remoteAddress, other.remotePort, other.localPort); + defineProps(other, tun); + //console.log('[hit tcp connection]', other.remoteFamily, other.remoteAddress, other.remotePort, other.localPort); + if (!state.greenlock) { state.greenlock = require('greenlock').create(state.greenlockConfig); } if (!state.terminatorServer) { state.terminatorServer = require('tls').createServer(state.greenlock.tlsOptions, function (tlsSocket) { - console.log('[hit tls server]'); - if (err) { cb(err); return; } - handlers.https(tlsSocket); + var Packer = require('proxy-packer'); + var addr = Packer.socketToAddr(tlsSocket); + var id = Packer.addrToId(addr); + + defineProps(tlsSocket, addr); + //console.log('[hit tls server]', tlsSocket.remoteFamily, tlsSocket.remoteAddress, tlsSocket.remotePort, tlsSocket.localPort); + //console.log(addr); + var conf = state.config.servernames[tlsSocket.servername]; + if (!conf || !conf.handler) { + handlers.https(tlsSocket); + return; + } + if (parseInt(conf.handler, 10)) { + // TODO http-proxy with proper headers and ws support + var conn = getNetConn(conf.handler); + conn.pipe(tlsSocket); + tlsSocket.pipe(conn); + } + var handler; + try { + handler = require(conf.handler); + handler(tlsSocket, addr, id); + } catch(e) { + handlers.https(tlsSocket, addr, id); + } }); } - console.log('[emitting tls connection]'); + //console.log('[hit tcp connection]', other.remoteFamily, other.remoteAddress, other.remotePort, other.localPort); state.terminatorServer.emit('connection', other); cb(null, conn); }); @@ -168,16 +247,16 @@ module.exports.assign = function (state, tun, cb) { } console.log('Terminating TLS for', tun.name); - terminateTls(cb); + terminateTls(tun, cb); return true; }); if (!handled) { - terminateTls(cb); + terminateTls(tun, cb); } return; } - return; + /* var portList = state.services[service]; var port; port = portList[tun.name]; @@ -199,72 +278,5 @@ module.exports.assign = function (state, tun, cb) { if (!port) { port = portList['*']; } - - var createOpts = { - port: port - , host: '127.0.0.1' - - , servername: tun.name - , name: tun.name - , serviceport: tun.serviceport - , data: tun.data - , remoteFamily: tun.family - , remoteAddress: tun.address - , remotePort: tun.port - }; - var conn; - - function handleNow(socket) { - var httpServer; - var tlsServer; - if ('https' === tun.service) { - if (!state.greenlock) { - state.greenlock = require('greenlock').create(state.greenlockConfig); - } - httpServer = require('http').createServer(function (req, res) { - console.log('[hit http/s server]'); - res.end('Hello, Encrypted Tunnel World!'); - }); - tlsServer = require('tls').createServer(state.greenlock.tlsOptions, function (tlsSocket) { - console.log('[hit tls server]'); - httpServer.emit('connection', tlsSocket); - }); - tlsServer.emit('connection', socket); - } else { - httpServer = require('http').createServer(state.greenlock.middleware(function (req, res) { - console.log('[hit pure http server]'); - res.end('Hello, Encrypted Tunnel World!'); - })); - // http://aj.telebit.cloud/.well-known/acme-challenge/blah - httpServer.emit('connection', socket); - } - } - if ('aj.telebit.cloud' === tun.name) { - console.log('NEW CONNECTION to AJ\'s telebit could'); - // For performance it may be better to use socket-pair, needs testing - var socketPair = require('socket-pair'); - conn = socketPair.create(function (err, other) { - if (err) { console.error('[Error] ' + err.message); } - handleNow(other); - //if (createOpts.data) { conn.write(createOpts.data); } - }); - /* - var streamPair = require('stream-pair'); - var pair = streamPair.create(); - conn = pair.other; - process.nextTick(function () { - if (createOpts.data) { - conn.write(createOpts.data); - } - }); - */ - } else { - conn = net.createConnection(createOpts, function () { - // this will happen before 'data' or 'readable' is triggered - // We use the data from the createOpts object so that the createConnection function has - // the oppurtunity of removing/changing it if it wants/needs to handle it differently. - //if (createOpts.data) { conn.write(createOpts.data); } - }); - } - cb(null, conn); + */ };