From ae15111b0f47f5da33b7a1fa43fbcee63c9decdc Mon Sep 17 00:00:00 2001 From: tigerbot Date: Mon, 3 Apr 2017 15:11:59 -0600 Subject: [PATCH] changed method for mapping services to local ports --- bin/stunnel.js | 104 ++++++++++++++++++++++++------------------------- wsclient.js | 28 +++++++------ 2 files changed, 63 insertions(+), 69 deletions(-) diff --git a/bin/stunnel.js b/bin/stunnel.js index 61eab40..6e48968 100755 --- a/bin/stunnel.js +++ b/bin/stunnel.js @@ -13,7 +13,6 @@ function collectDomains(val, memo) { function parseProxy(location) { // john.example.com - // https:3443 // http:john.example.com:3000 // http://john.example.com:3000 var parts = location.split(':'); @@ -144,19 +143,11 @@ function connectTunnel() { } }; - program.locals.forEach(function (proxy) { - var port = proxy.port; - if (!proxy.port) { - if ('http' === proxy.protocol) { - port = hasHttp; - } - else if ('https' === proxy.protocol) { - port = hasHttps; - } - } - if (proxy.protocol) { - console.info('[local proxy]', proxy.protocol + '://' + proxy.hostname + ' => ' + port); - } + Object.keys(program.services).forEach(function (protocol) { + var subServices = program.services[protocol]; + Object.keys(subServices).forEach(function (hostname) { + console.info('[local proxy]', protocol + '://' + hostname + ' => ' + subServices[hostname]); + }); }); console.info(''); @@ -179,22 +170,22 @@ function rawTunnel() { return; } - var jwt = require('jsonwebtoken'); - var tokenData = { - domains: null - }; - var location = url.parse(program.stunneld); + if (!program.token) { + var jwt = require('jsonwebtoken'); + var tokenData = { + domains: Object.keys(domainsMap).filter(Boolean) + }; + program.token = jwt.sign(tokenData, program.secret); + } + + var location = url.parse(program.stunneld); if (!location.protocol || /\./.test(location.protocol)) { program.stunneld = 'wss://' + program.stunneld; location = url.parse(program.stunneld); } program.stunneld = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : ''); - tokenData.domains = Object.keys(domainsMap).filter(Boolean); - - program.token = program.token || jwt.sign(tokenData, program.secret); - connectTunnel(); } @@ -234,45 +225,50 @@ function daplieTunnel() { } var domainsMap = {}; -var hasHttp; -var hasHttps; +var services = {}; -program.locals = program.locals || []; -program.locals = program.locals.concat(program.domains || []); +program.locals = (program.locals || []).concat(program.domains || []); program.locals.forEach(function (proxy) { - if ('*' === proxy.hostname) { - if ('http' === proxy.protocol) { - hasHttp = proxy.port; + // Create a map from which we can derive a list of all domains we want forwarded to us. + if (proxy.hostname && proxy.hostname !== '*') { + domainsMap[proxy.hostname] = true; + } + + // Create a map of which port different protocols should be forwarded to, allowing for specific + // domains to go to different ports if need be (though that only works for HTTP and HTTPS). + if (proxy.protocol && proxy.port) { + services[proxy.protocol] = services[proxy.protocol] || {}; + + if (/http/.test(proxy.protocol) && proxy.hostname && proxy.hostname !== '*') { + services[proxy.protocol][proxy.hostname] = proxy.port; } - else if ('https' === proxy.protocol) { - hasHttps = proxy.port; + else { + if (services[proxy.protocol]['*'] && services[proxy.protocol]['*'] !== proxy.port) { + console.error('cannot forward generic', proxy.protocol, 'traffic to multiple ports'); + process.exit(1); + } + else { + services[proxy.protocol]['*'] = proxy.port; + } } } }); -if (!hasHttp) { - program.locals.push({ - protocol: 'http' - , hostname: '*' - , port: 8443 - }); - hasHttp = 8443; -} -if (!hasHttps) { - program.locals.push({ - protocol: 'https' - , hostname: '*' - , port: 8443 - }); - hasHttps = 8443; -} -program.locals.forEach(function (proxy) { - domainsMap[proxy.hostname] = true; -}); -if (domainsMap.hasOwnProperty('*')) { - delete domainsMap['*']; - //domainsMap['*'] = false; + +if (Object.keys(domainsMap).length === 0) { + console.error('no domains specified'); + process.exit(1); + return; } +// Make sure we have generic ports for HTTP and HTTPS +services.https = services.https || {}; +services.https['*'] = services.https['*'] || 8443; + +services.http = services.http || {}; +services.http['*'] = services.http['*'] || services.https['*']; + +program.services = services; + if (!(program.secret || program.token) && !program.stunneld) { daplieTunnel(); } diff --git a/wsclient.js b/wsclient.js index c96aedc..4d85e99 100644 --- a/wsclient.js +++ b/wsclient.js @@ -7,13 +7,6 @@ var Packer = require('tunnel-packer'); var authenticated = false; function run(copts) { - // TODO pair with hostname / sni - copts.services = {}; - copts.locals.forEach(function (proxy) { - //program.services = { 'ssh': 22, 'http': 80, 'https': 443 }; - copts.services[proxy.protocol] = proxy.port; - }); - var tunnelUrl = copts.stunneld.replace(/\/$/, '') + '/?access_token=' + copts.token; var wstunneler; var localclients = {}; @@ -26,9 +19,10 @@ function run(copts) { onmessage: function (opts) { var net = copts.net || require('net'); var cid = Packer.addrToId(opts); - var service = opts.service; - var port = copts.services[service]; + var service = opts.service.toLowerCase(); + var portList = copts.services[service]; var servername; + var port; var str; var m; @@ -39,7 +33,12 @@ function run(copts) { localclients[cid].write(opts.data); return; } - else if ('http' === service) { + if (!portList) { + handlers._onLocalError(cid, opts, new Error("unsupported service '" + service + "'")); + return; + } + + if ('http' === service) { str = opts.data.toString(); m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im); servername = (m && m[1].toLowerCase() || '').split(':')[0]; @@ -48,20 +47,19 @@ function run(copts) { servername = sni(opts.data); } else { - handlers._onLocalError(cid, opts, new Error("unsupported service '" + service + "'")); - return; + servername = '*'; } if (!servername) { - console.info("[error] missing servername for '" + cid + "'", opts.data.byteLength); //console.warn(opts.data.toString()); - wstunneler.send(Packer.pack(opts, null, 'error'), { binary: true }); + handlers._onLocalError(cid, opts, new Error("missing servername for '" + cid + "' " + opts.data.byteLength)); return; } + port = portList[servername] || portList['*']; console.info("[connect] new client '" + cid + "' for '" + servername + "' (" + (handlers._numClients() + 1) + " clients)"); - console.log('port', port, opts.port, service, copts.services); + console.log('port', port, opts.port, service, portList); localclients[cid] = net.createConnection({ port: port , host: '127.0.0.1'