commit
5503714b53
121
bin/stunneld.js
121
bin/stunneld.js
|
@ -6,6 +6,15 @@ var pkg = require('../package.json');
|
||||||
|
|
||||||
var program = require('commander');
|
var program = require('commander');
|
||||||
var stunneld = require('../wstunneld.js');
|
var stunneld = require('../wstunneld.js');
|
||||||
|
var greenlock = require('greenlock');
|
||||||
|
|
||||||
|
function collectServernames(val, memo) {
|
||||||
|
val.split(/,/).forEach(function (servername) {
|
||||||
|
memo.push(servername.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
}
|
||||||
|
|
||||||
function collectProxies(val, memo) {
|
function collectProxies(val, memo) {
|
||||||
var vals = val.split(/,/g);
|
var vals = val.split(/,/g);
|
||||||
|
@ -62,16 +71,15 @@ function collectPorts(val, memo) {
|
||||||
|
|
||||||
program
|
program
|
||||||
.version(pkg.version)
|
.version(pkg.version)
|
||||||
|
.option('--agree-tos', "Accept the Daplie and Let's Encrypt Terms of Service")
|
||||||
|
.option('--email <EMAIL>', "Email to use for Daplie and Let's Encrypt accounts")
|
||||||
.option('--serve <URL>', 'comma separated list of <proto>:<//><servername>:<port> to which matching incoming http and https should forward (reverse proxy). Ex: https://john.example.com,tls:*:1337', collectProxies, [ ])
|
.option('--serve <URL>', 'comma separated list of <proto>:<//><servername>:<port> to which matching incoming http and https should forward (reverse proxy). Ex: https://john.example.com,tls:*:1337', collectProxies, [ ])
|
||||||
.option('--ports <PORT>', 'comma separated list of ports on which to listen. Ex: 80,443,1337', collectPorts, [ ])
|
.option('--ports <PORT>', 'comma separated list of ports on which to listen. Ex: 80,443,1337', collectPorts, [ ])
|
||||||
|
.option('--servernames <STRING>', 'comma separated list of servernames to use for the admin interface. Ex: tunnel.example.com,tunnel.example.net', collectServernames, [ ])
|
||||||
.option('--secret <STRING>', 'the same secret used by stunneld (used for JWT authentication)')
|
.option('--secret <STRING>', 'the same secret used by stunneld (used for JWT authentication)')
|
||||||
.parse(process.argv)
|
.parse(process.argv)
|
||||||
;
|
;
|
||||||
|
|
||||||
if (!program.serve.length) {
|
|
||||||
throw new Error("must specify at least one server");
|
|
||||||
}
|
|
||||||
|
|
||||||
var portsMap = {};
|
var portsMap = {};
|
||||||
var servernamesMap = {};
|
var servernamesMap = {};
|
||||||
program.serve.forEach(function (proxy) {
|
program.serve.forEach(function (proxy) {
|
||||||
|
@ -80,27 +88,108 @@ program.serve.forEach(function (proxy) {
|
||||||
portsMap[proxy.port] = true;
|
portsMap[proxy.port] = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
program.servernames.forEach(function (name) {
|
||||||
|
servernamesMap[name] = true;
|
||||||
|
});
|
||||||
program.ports.forEach(function (port) {
|
program.ports.forEach(function (port) {
|
||||||
portsMap[port] = true;
|
portsMap[port] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
var opts = {};
|
program.servernames = Object.keys(servernamesMap);
|
||||||
opts.servernames = Object.keys(servernamesMap);
|
if (!program.servernames.length) {
|
||||||
opts.ports = Object.keys(portsMap);
|
throw new Error('must specify at least one server or servername');
|
||||||
if (!opts.ports.length) {
|
|
||||||
opts.ports = [ 80, 443 ];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (program.secret) {
|
program.ports = Object.keys(portsMap);
|
||||||
opts.secret = program.secret;
|
if (!opts.ports.length) {
|
||||||
} else {
|
program.ports = [ 80, 443 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!program.secret) {
|
||||||
// TODO randomly generate and store in file?
|
// TODO randomly generate and store in file?
|
||||||
console.warn("[SECURITY] using default --secret 'shhhhh'");
|
console.warn("[SECURITY] you must provide --secret '" + require('crypto').randomBytes(16).toString('hex') + "'");
|
||||||
opts.secret = 'shhhhh';
|
process.exit(1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO letsencrypt
|
// TODO letsencrypt
|
||||||
opts.tlsOptions = require('localhost.daplie.com-certificates').merge({});
|
program.tlsOptions = require('localhost.daplie.com-certificates').merge({});
|
||||||
|
|
||||||
|
function approveDomains(opts, certs, cb) {
|
||||||
|
// This is where you check your database and associated
|
||||||
|
// email addresses with domains and agreements and such
|
||||||
|
|
||||||
|
// The domains being approved for the first time are listed in opts.domains
|
||||||
|
// Certs being renewed are listed in certs.altnames
|
||||||
|
if (certs) {
|
||||||
|
opts.domains = certs.altnames;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (-1 !== program.servernames.indexOf(opts.domain)) {
|
||||||
|
opts.email = program.email;
|
||||||
|
opts.agreeTos = program.agreeTos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
||||||
|
// opts.challengeType = 'http-01';
|
||||||
|
// opts.challenge = require('le-challenge-fs').create({});
|
||||||
|
|
||||||
|
cb(null, { options: opts, certs: certs });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!program.email || !program.agreeTos) {
|
||||||
|
console.error("You didn't specify --email <EMAIL> and --agree-tos");
|
||||||
|
console.error("(required for ACME / Let's Encrypt / Greenlock TLS/SSL certs)");
|
||||||
|
console.error("");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
program.greenlock = greenlock.create({
|
||||||
|
|
||||||
|
//server: 'staging'
|
||||||
|
server: 'https://acme-v01.api.letsencrypt.org/directory'
|
||||||
|
|
||||||
|
, challenges: {
|
||||||
|
// TODO dns-01
|
||||||
|
'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' })
|
||||||
|
}
|
||||||
|
|
||||||
|
, store: require('le-store-certbot').create({ webrootPath: '/tmp/acme-challenges' })
|
||||||
|
|
||||||
|
, email: program.email
|
||||||
|
|
||||||
|
, agreeTos: program.agreeTos
|
||||||
|
|
||||||
|
, approveDomains: approveDomains
|
||||||
|
|
||||||
|
//, approvedDomains: program.servernames
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//program.tlsOptions.SNICallback = program.greenlock.httpsOptions.SNICallback;
|
||||||
|
/*
|
||||||
|
program.middleware = program.greenlock.middleware(function (req, res) {
|
||||||
|
res.end('Hello, World!');
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
require('../handlers').create(program); // adds directly to program for now...
|
||||||
|
|
||||||
|
//require('cluster-store').create().then(function (store) {
|
||||||
|
//program.store = store;
|
||||||
|
|
||||||
|
var net = require('net');
|
||||||
|
var netConnHandlers = stunneld.create(program); // { tcp, ws }
|
||||||
|
var WebSocketServer = require('ws').Server;
|
||||||
|
var wss = new WebSocketServer({ server: (program.httpTunnelServer || program.httpServer) });
|
||||||
|
wss.on('connection', netConnHandlers.ws);
|
||||||
|
program.ports.forEach(function (port) {
|
||||||
|
var tcp3000 = net.createServer();
|
||||||
|
tcp3000.listen(port, function () {
|
||||||
|
console.log('listening on ' + port);
|
||||||
|
});
|
||||||
|
tcp3000.on('connection', netConnHandlers.tcp);
|
||||||
|
});
|
||||||
|
//});
|
||||||
|
|
||||||
stunneld.create(opts);
|
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var http = require('http');
|
||||||
|
var tls = require('tls');
|
||||||
|
var packerStream = require('tunnel-packer').Stream;
|
||||||
|
var redirectHttps = require('redirect-https')();
|
||||||
|
|
||||||
|
module.exports.create = function (program) {
|
||||||
|
var tunnelAdminTlsOpts = {};
|
||||||
|
|
||||||
|
// Probably a reverse proxy on an internal network (or ACME challenge)
|
||||||
|
function notFound(req, res) {
|
||||||
|
console.log('req.socket.encrypted', req.socket.encrypted);
|
||||||
|
res.statusCode = 404;
|
||||||
|
res.end("File not found.\n");
|
||||||
|
}
|
||||||
|
program.httpServer = http.createServer(
|
||||||
|
program.greenlock && program.greenlock.middleware(notFound)
|
||||||
|
|| notFound
|
||||||
|
);
|
||||||
|
program.handleHttp = function (servername, socket) {
|
||||||
|
console.log("handleHttp('" + servername + "', socket)");
|
||||||
|
socket.__my_servername = servername;
|
||||||
|
program.httpServer.emit('connection', socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Probably something that needs to be redirected to https
|
||||||
|
function redirectHttpsAndClose(req, res) {
|
||||||
|
res.setHeader('Connection', 'close');
|
||||||
|
redirectHttps(req, res);
|
||||||
|
}
|
||||||
|
program.httpInsecureServer = http.createServer(
|
||||||
|
program.greenlock && program.greenlock.middleware(redirectHttpsAndClose)
|
||||||
|
|| redirectHttpsAndClose
|
||||||
|
);
|
||||||
|
program.handleInsecureHttp = function (servername, socket) {
|
||||||
|
console.log("handleInsecureHttp('" + servername + "', socket)");
|
||||||
|
socket.__my_servername = servername;
|
||||||
|
program.httpInsecureServer.emit('connection', socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// SNI is not recogonized / cannot be handled
|
||||||
|
//
|
||||||
|
program.httpInvalidSniServer = http.createServer(function (req, res) {
|
||||||
|
res.end("You're doing strange things that make me feel uncomfortable. Please don't touch me there any more.");
|
||||||
|
});
|
||||||
|
program.tlsInvalidSniServer = tls.createServer(program.tlsOptions, function (tlsSocket) {
|
||||||
|
console.log('tls connection');
|
||||||
|
// things get a little messed up here
|
||||||
|
program.httpInvalidSniServer.emit('connection', tlsSocket);
|
||||||
|
});
|
||||||
|
program.httpsInvalid = function (servername, socket) {
|
||||||
|
// none of these methods work:
|
||||||
|
// httpsServer.emit('connection', socket); // this didn't work
|
||||||
|
// tlsServer.emit('connection', socket); // this didn't work either
|
||||||
|
//console.log('chunkLen', firstChunk.byteLength);
|
||||||
|
|
||||||
|
var myDuplex = packerStream.create(socket);
|
||||||
|
|
||||||
|
console.log('httpsInvalid servername', servername);
|
||||||
|
program.tlsInvalidSniServer.emit('connection', myDuplex);
|
||||||
|
|
||||||
|
socket.on('data', function (chunk) {
|
||||||
|
console.log('[' + Date.now() + '] socket data', chunk.byteLength);
|
||||||
|
myDuplex.push(chunk);
|
||||||
|
});
|
||||||
|
socket.on('error', function (err) {
|
||||||
|
console.error('[error] httpsInvalid TODO close');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// To ADMIN / CONTROL PANEL of the Tunnel Server Itself
|
||||||
|
//
|
||||||
|
program.httpTunnelServer = http.createServer(function (req, res) {
|
||||||
|
console.log('req.socket.encrypted', req.socket.encrypted);
|
||||||
|
res.end('Hello, World!');
|
||||||
|
});
|
||||||
|
Object.keys(program.tlsOptions).forEach(function (key) {
|
||||||
|
tunnelAdminTlsOpts[key] = program.tlsOptions[key];
|
||||||
|
});
|
||||||
|
tunnelAdminTlsOpts.SNICallback = (program.greenlock && program.greenlock.httpsOptions && function (servername, cb) {
|
||||||
|
console.log("time to handle '" + servername + "'");
|
||||||
|
program.greenlock.httpsOptions.SNICallback(servername, cb);
|
||||||
|
}) || tunnelAdminTlsOpts.SNICallback;
|
||||||
|
program.tlsTunnelServer = tls.createServer(tunnelAdminTlsOpts, function (tlsSocket) {
|
||||||
|
console.log('tls connection');
|
||||||
|
// things get a little messed up here
|
||||||
|
(program.httpTunnelServer || program.httpServer).emit('connection', tlsSocket);
|
||||||
|
});
|
||||||
|
program.httpsTunnel = function (servername, socket) {
|
||||||
|
// none of these methods work:
|
||||||
|
// httpsServer.emit('connection', socket); // this didn't work
|
||||||
|
// tlsServer.emit('connection', socket); // this didn't work either
|
||||||
|
//console.log('chunkLen', firstChunk.byteLength);
|
||||||
|
|
||||||
|
var myDuplex = packerStream.create(socket);
|
||||||
|
|
||||||
|
console.log('httpsTunnel (Admin) servername', servername);
|
||||||
|
program.tlsTunnelServer.emit('connection', myDuplex);
|
||||||
|
|
||||||
|
socket.on('data', function (chunk) {
|
||||||
|
console.log('[' + Date.now() + '] socket data', chunk.byteLength);
|
||||||
|
myDuplex.push(chunk);
|
||||||
|
});
|
||||||
|
socket.on('error', function (err) {
|
||||||
|
console.error('[error] httpsTunnel (Admin) TODO close');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -48,6 +48,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cluster-store": "^2.0.4",
|
"cluster-store": "^2.0.4",
|
||||||
"commander": "^2.9.0",
|
"commander": "^2.9.0",
|
||||||
|
"greenlock": "^2.1.12",
|
||||||
"jsonwebtoken": "^7.1.9",
|
"jsonwebtoken": "^7.1.9",
|
||||||
"localhost.daplie.com-certificates": "^1.2.3",
|
"localhost.daplie.com-certificates": "^1.2.3",
|
||||||
"redirect-https": "^1.1.0",
|
"redirect-https": "^1.1.0",
|
||||||
|
|
208
wstunneld.js
208
wstunneld.js
|
@ -1,23 +1,86 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var net = require('net');
|
|
||||||
var tls = require('tls');
|
|
||||||
var http = require('http');
|
|
||||||
var sni = require('sni');
|
var sni = require('sni');
|
||||||
var url = require('url');
|
var url = require('url');
|
||||||
var jwt = require('jsonwebtoken');
|
var jwt = require('jsonwebtoken');
|
||||||
var packer = require('tunnel-packer');
|
var packer = require('tunnel-packer');
|
||||||
var WebSocketServer = require('ws').Server;
|
|
||||||
|
|
||||||
|
var Devices = {};
|
||||||
|
Devices.replace = function (store, servername, newDevice) {
|
||||||
|
var devices = Devices.list(store, servername);
|
||||||
|
var oldDevice;
|
||||||
|
if (!devices.some(function (device, i) {
|
||||||
|
if ((device.deviceId && device.deviceId === newDevice.deviceId)
|
||||||
|
|| (device.servername && device.servername === newDevice.servername)) {
|
||||||
|
oldDevice = devices[i];
|
||||||
|
devices[i] = newDevice;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})) {
|
||||||
|
devices.push(newDevice);
|
||||||
|
store[servername] = devices;
|
||||||
|
}
|
||||||
|
return oldDevice;
|
||||||
|
};
|
||||||
|
Devices.remove = function (store, servername, newDevice) {
|
||||||
|
var devices = Devices.list(store, servername);
|
||||||
|
var oldDevice;
|
||||||
|
devices.some(function (device, i) {
|
||||||
|
if ((device.deviceId && device.deviceId === newDevice.deviceId)
|
||||||
|
|| (device.servername && device.servername === newDevice.servername)) {
|
||||||
|
oldDevice = devices.splice(i, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return oldDevice;
|
||||||
|
};
|
||||||
|
Devices.list = function (store, servername) {
|
||||||
|
return store[servername] || [];
|
||||||
|
};
|
||||||
|
Devices.exist = function (store, servername) {
|
||||||
|
return (store[servername] || []).length;
|
||||||
|
};
|
||||||
|
Devices.next = function (store, servername) {
|
||||||
|
var devices = Devices.list(store, servername);
|
||||||
|
var device;
|
||||||
|
|
||||||
|
if (devices._index >= devices.length) {
|
||||||
|
devices._index = 0;
|
||||||
|
}
|
||||||
|
device = devices[devices._index || 0];
|
||||||
|
devices._index = (devices._index || 0) + 1;
|
||||||
|
|
||||||
|
return device;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.store = { Devices: Devices };
|
||||||
module.exports.create = function (copts) {
|
module.exports.create = function (copts) {
|
||||||
|
var deviceLists = {};
|
||||||
|
|
||||||
function onWsConnection(ws) {
|
function onWsConnection(ws) {
|
||||||
var location = url.parse(ws.upgradeReq.url, true);
|
var location = url.parse(ws.upgradeReq.url, true);
|
||||||
//var token = jwt.decode(location.query.access_token);
|
var authn = (ws.upgradeReq.headers.authorization||'').split(/\s+/);
|
||||||
|
var jwtoken;
|
||||||
var token;
|
var token;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
token = jwt.verify(location.query.access_token, secret);
|
if (authn[0]) {
|
||||||
|
if ('basic' === authn[0].toLowerCase()) {
|
||||||
|
authn = new Buffer(authn[1], 'base64').toString('ascii').split(':');
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (-1 !== [ 'bearer', 'jwk' ].indexOf(authn[0].toLowerCase())) {
|
||||||
|
jwtoken = authn[1];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
jwtoken = authn[1] || location.query.access_token;
|
||||||
|
} catch(e) {
|
||||||
|
jwtoken = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
token = jwt.verify(jwtoken, copts.secret);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
token = null;
|
token = null;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +99,8 @@ module.exports.create = function (copts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//console.log('[wstunneld.js] DEBUG', token);
|
||||||
|
|
||||||
if (!Array.isArray(token.domains)) {
|
if (!Array.isArray(token.domains)) {
|
||||||
if ('string' === typeof token.name) {
|
if ('string' === typeof token.name) {
|
||||||
token.domains = [ token.name ];
|
token.domains = [ token.name ];
|
||||||
|
@ -50,13 +115,13 @@ module.exports.create = function (copts) {
|
||||||
|
|
||||||
var remote;
|
var remote;
|
||||||
token.domains.some(function (domainname) {
|
token.domains.some(function (domainname) {
|
||||||
remote = remotes[domainname];
|
remote = Devices.next(deviceLists, domainname);
|
||||||
return remote;
|
return remote;
|
||||||
});
|
});
|
||||||
remote = remote || {};
|
remote = remote || {};
|
||||||
token.domains.forEach(function (domainname) {
|
token.domains.forEach(function (domainname) {
|
||||||
console.log('domainname', domainname);
|
console.log('domainname', domainname);
|
||||||
remotes[domainname] = remote;
|
Devices.replace(deviceLists, domainname, remote);
|
||||||
});
|
});
|
||||||
var handlers = {
|
var handlers = {
|
||||||
onmessage: function (opts) {
|
onmessage: function (opts) {
|
||||||
|
@ -100,7 +165,8 @@ module.exports.create = function (copts) {
|
||||||
};
|
};
|
||||||
// TODO allow more than one remote per servername
|
// TODO allow more than one remote per servername
|
||||||
remote.ws = ws;
|
remote.ws = ws;
|
||||||
remote.servername = token.domains.join(',');
|
remote.servername = (token.device && token.device.hostname) || token.domains.join(',');
|
||||||
|
remote.deviceId = (token.device && token.device.id) || null;
|
||||||
remote.id = packer.socketToId(ws.upgradeReq.socket);
|
remote.id = packer.socketToId(ws.upgradeReq.socket);
|
||||||
console.log("remote.id", remote.id);
|
console.log("remote.id", remote.id);
|
||||||
// TODO allow tls to be decrypted by server if client is actually a browser
|
// TODO allow tls to be decrypted by server if client is actually a browser
|
||||||
|
@ -110,12 +176,14 @@ module.exports.create = function (copts) {
|
||||||
remote.clients = {};
|
remote.clients = {};
|
||||||
remote.handle = { address: null, handle: null };
|
remote.handle = { address: null, handle: null };
|
||||||
remote.unpacker = packer.create(handlers);
|
remote.unpacker = packer.create(handlers);
|
||||||
ws.on('message', function (chunk) {
|
remote.domains = token.domains;
|
||||||
|
|
||||||
|
function forwardMessage(chunk) {
|
||||||
console.log('message from home cloud to tunneler to browser', chunk.byteLength);
|
console.log('message from home cloud to tunneler to browser', chunk.byteLength);
|
||||||
//console.log(chunk.toString());
|
//console.log(chunk.toString());
|
||||||
remote.unpacker.fns.addChunk(chunk);
|
remote.unpacker.fns.addChunk(chunk);
|
||||||
});
|
}
|
||||||
ws.on('close', function () {
|
function hangup() {
|
||||||
// the remote will handle closing its local connections
|
// the remote will handle closing its local connections
|
||||||
Object.keys(remote.clients).forEach(function (cid) {
|
Object.keys(remote.clients).forEach(function (cid) {
|
||||||
try {
|
try {
|
||||||
|
@ -124,46 +192,23 @@ module.exports.create = function (copts) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
token.domains.forEach(function (domainname) {
|
||||||
ws.on('error', function () {
|
Devices.remove(deviceLists, domainname, remote);
|
||||||
// ignore
|
});
|
||||||
// the remote will retry if it wants to
|
}
|
||||||
});
|
function die() {
|
||||||
|
hangup();
|
||||||
|
}
|
||||||
|
|
||||||
//store.set(token.name, remote.handle);
|
ws.on('message', forwardMessage);
|
||||||
|
ws.on('close', hangup);
|
||||||
|
ws.on('error', die);
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectHttp(servername, socket) {
|
|
||||||
console.log("connectHttp('" + servername + "', socket)");
|
|
||||||
socket.__my_servername = servername;
|
|
||||||
redirectServer.emit('connection', socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectHttps(servername, socket) {
|
|
||||||
// none of these methods work:
|
|
||||||
// httpsServer.emit('connection', socket); // this didn't work
|
|
||||||
// tlsServer.emit('connection', socket); // this didn't work either
|
|
||||||
//console.log('chunkLen', firstChunk.byteLength);
|
|
||||||
|
|
||||||
var myDuplex = packer.Stream.create(socket);
|
|
||||||
|
|
||||||
console.log('connectHttps servername', servername);
|
|
||||||
tls3000.emit('connection', myDuplex);
|
|
||||||
|
|
||||||
socket.on('data', function (chunk) {
|
|
||||||
console.log('[' + Date.now() + '] socket data', chunk.byteLength);
|
|
||||||
myDuplex.push(chunk);
|
|
||||||
});
|
|
||||||
socket.on('error', function (err) {
|
|
||||||
console.error('[error] connectHttps TODO close');
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function pipeWs(servername, service, browser, remote) {
|
function pipeWs(servername, service, browser, remote) {
|
||||||
console.log('pipeWs');
|
console.log('pipeWs');
|
||||||
|
|
||||||
//var remote = remotes[servername];
|
//var remote = deviceLists[servername];
|
||||||
var ws = remote.ws;
|
var ws = remote.ws;
|
||||||
//var address = packer.socketToAddr(ws.upgradeReq.socket);
|
//var address = packer.socketToAddr(ws.upgradeReq.socket);
|
||||||
var baddress = packer.socketToAddr(browser);
|
var baddress = packer.socketToAddr(browser);
|
||||||
|
@ -259,14 +304,29 @@ module.exports.create = function (copts) {
|
||||||
var m;
|
var m;
|
||||||
|
|
||||||
function tryTls() {
|
function tryTls() {
|
||||||
if (!servername || (-1 !== selfnames.indexOf(servername)) || !remotes[servername]) {
|
var nextDevice;
|
||||||
console.log('this is a server or an unknown');
|
|
||||||
connectHttps(servername, browser);
|
if (-1 !== copts.servernames.indexOf(servername)) {
|
||||||
|
console.log("Lock and load, admin interface time!");
|
||||||
|
copts.httpsTunnel(servername, browser);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("pipeWs(servername, service, socket, remotes['" + servername + "'])");
|
if (!servername) {
|
||||||
pipeWs(servername, service, browser, remotes[servername]);
|
console.log("No SNI was given, so there's nothing we can do here");
|
||||||
|
copts.httpsInvalid(servername, browser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextDevice = Devices.next(deviceLists, servername);
|
||||||
|
if (!nextDevice) {
|
||||||
|
console.log("No devices match the given servername");
|
||||||
|
copts.httpsInvalid(servername, browser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("pipeWs(servername, service, socket, deviceLists['" + servername + "'])");
|
||||||
|
pipeWs(servername, service, browser, nextDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
|
// https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
|
||||||
|
@ -286,17 +346,19 @@ module.exports.create = function (copts) {
|
||||||
console.log('servername', servername);
|
console.log('servername', servername);
|
||||||
if (/HTTP\//i.test(str)) {
|
if (/HTTP\//i.test(str)) {
|
||||||
service = 'http';
|
service = 'http';
|
||||||
if (/^\/\.well-known\/acme-challenge\//.test(str)) {
|
// TODO disallow http entirely
|
||||||
|
// /^\/\.well-known\/acme-challenge\//.test(str)
|
||||||
|
if (/well-known/.test(str)) {
|
||||||
// HTTP
|
// HTTP
|
||||||
if (remotes[servername]) {
|
if (Devices.exist(deviceLists, servername)) {
|
||||||
pipeWs(servername, service, browser, remotes[servername]);
|
pipeWs(servername, service, browser, Devices.next(deviceLists, servername));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
connectHttp(servername, browser);
|
copts.handleHttp(servername, browser);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// redirect to https
|
// redirect to https
|
||||||
connectHttp(servername, browser);
|
copts.handleInsecureHttp(servername, browser);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -316,37 +378,5 @@ module.exports.create = function (copts) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var tlsOpts = copts.tlsOptions;
|
return { tcp: onTcpConnection, ws: onWsConnection };
|
||||||
//var store = copts.store;
|
|
||||||
|
|
||||||
var remotes = {};
|
|
||||||
var selfnames = copts.servernames;
|
|
||||||
var secret = copts.secret;
|
|
||||||
var redirectHttps = require('redirect-https')();
|
|
||||||
|
|
||||||
var redirectServer = http.createServer(function (req, res) {
|
|
||||||
res.setHeader('Connection', 'close');
|
|
||||||
redirectHttps(req, res);
|
|
||||||
});
|
|
||||||
var httpServer = http.createServer(function (req, res) {
|
|
||||||
console.log('req.socket.encrypted', req.socket.encrypted);
|
|
||||||
res.end('Hello, World!');
|
|
||||||
});
|
|
||||||
var tls3000 = tls.createServer(tlsOpts, function (tlsSocket) {
|
|
||||||
console.log('tls connection');
|
|
||||||
// things get a little messed up here
|
|
||||||
httpServer.emit('connection', tlsSocket);
|
|
||||||
});
|
|
||||||
var wss = new WebSocketServer({ server: httpServer });
|
|
||||||
|
|
||||||
wss.on('connection', onWsConnection);
|
|
||||||
|
|
||||||
copts.ports.forEach(function (port) {
|
|
||||||
var tcp3000 = net.createServer();
|
|
||||||
tcp3000.listen(port, function () {
|
|
||||||
console.log('listening on ' + port);
|
|
||||||
});
|
|
||||||
tcp3000.on('connection', onTcpConnection);
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue