Compare commits
8 Commits
commercial
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a395a299a | |||
|
|
eb36af8269 | ||
| 50c0449206 | |||
| d48707d265 | |||
| 60f85144a9 | |||
| 5dfe25ed95 | |||
| c9d6b46f0f | |||
| 0a67728239 |
9
.gitignore
vendored
9
.gitignore
vendored
@ -43,3 +43,12 @@ jspm_packages
|
|||||||
|
|
||||||
# Optional REPL history
|
# Optional REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
|
# Snapcraft
|
||||||
|
/parts/
|
||||||
|
/prime/
|
||||||
|
/stage/
|
||||||
|
.snapcraft
|
||||||
|
*.snap
|
||||||
|
*.tar.bz2
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,13 @@ function applyConfig(config) {
|
|||||||
} else {
|
} else {
|
||||||
state.Promise = require('bluebird');
|
state.Promise = require('bluebird');
|
||||||
}
|
}
|
||||||
state.tlsOptions = {}; // TODO just close the sockets that would use this early? or use the admin servername
|
state.tlsOptions = {
|
||||||
|
// Handles disconnected devices
|
||||||
|
// TODO allow user to opt-in to wildcard hosting for a better error page?
|
||||||
|
SNICallback: function (servername, cb) {
|
||||||
|
return state.greenlock.tlsOptions.SNICallback(state.config.webminDomain || state.servernames[0], cb);
|
||||||
|
}
|
||||||
|
}; // TODO just close the sockets that would use this early? or use the admin servername
|
||||||
state.config = config;
|
state.config = config;
|
||||||
state.servernames = config.servernames || [];
|
state.servernames = config.servernames || [];
|
||||||
state.secret = state.config.secret;
|
state.secret = state.config.secret;
|
||||||
|
|||||||
35
lib/ago-test.js
Normal file
35
lib/ago-test.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var timeago = require('./ago.js').AGO;
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
[ 1.5 * 1000 // a moment ago
|
||||||
|
, 4.5 * 1000 // moments ago
|
||||||
|
, 10 * 1000 // 10 seconds ago
|
||||||
|
, 59 * 1000 // a minute ago
|
||||||
|
, 60 * 1000 // a minute ago
|
||||||
|
, 61 * 1000 // a minute ago
|
||||||
|
, 119 * 1000 // a minute ago
|
||||||
|
, 120 * 1000 // 2 minutes ago
|
||||||
|
, 121 * 1000 // 2 minutes ago
|
||||||
|
, (60 * 60 * 1000) - 1000 // 59 minutes ago
|
||||||
|
, 1 * 60 * 60 * 1000 // an hour ago
|
||||||
|
, 1.5 * 60 * 60 * 1000 // an hour ago
|
||||||
|
, 2.5 * 60 * 60 * 1000 // 2 hours ago
|
||||||
|
, 1.5 * 24 * 60 * 60 * 1000 // a day ago
|
||||||
|
, 2.5 * 24 * 60 * 60 * 1000 // 2 days ago
|
||||||
|
, 7 * 24 * 60 * 60 * 1000 // a week ago
|
||||||
|
, 14 * 24 * 60 * 60 * 1000 // 2 weeks ago
|
||||||
|
, 27 * 24 * 60 * 60 * 1000 // 3 weeks ago
|
||||||
|
, 28 * 24 * 60 * 60 * 1000 // 4 weeks ago
|
||||||
|
, 29 * 24 * 60 * 60 * 1000 // 4 weeks ago
|
||||||
|
, 1.5 * 30 * 24 * 60 * 60 * 1000 // a month ago
|
||||||
|
, 2.5 * 30 * 24 * 60 * 60 * 1000 // 2 months ago
|
||||||
|
, (12 * 30 * 24 * 60 * 60 * 1000) + 1000 // 12 months ago
|
||||||
|
, 13 * 30 * 24 * 60 * 60 * 1000 // over a year ago
|
||||||
|
].forEach(function (d) {
|
||||||
|
console.log(d, '=', timeago(d));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test();
|
||||||
50
lib/ago.js
Normal file
50
lib/ago.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
;(function (exports) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
exports.AGO = function timeago(ms) {
|
||||||
|
var ago = Math.floor(ms / 1000);
|
||||||
|
var part = 0;
|
||||||
|
|
||||||
|
if (ago < 2) { return "a moment ago"; }
|
||||||
|
if (ago < 5) { return "moments ago"; }
|
||||||
|
if (ago < 60) { return ago + " seconds ago"; }
|
||||||
|
|
||||||
|
if (ago < 120) { return "a minute ago"; }
|
||||||
|
if (ago < 3600) {
|
||||||
|
while (ago >= 60) { ago -= 60; part += 1; }
|
||||||
|
return part + " minutes ago";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ago < 7200) { return "an hour ago"; }
|
||||||
|
if (ago < 86400) {
|
||||||
|
while (ago >= 3600) { ago -= 3600; part += 1; }
|
||||||
|
return part + " hours ago";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ago < 172800) { return "a day ago"; }
|
||||||
|
if (ago < 604800) {
|
||||||
|
while (ago >= 172800) { ago -= 172800; part += 1; }
|
||||||
|
return part + " days ago";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ago < 1209600) { return "a week ago"; }
|
||||||
|
if (ago < 2592000) {
|
||||||
|
while (ago >= 604800) { ago -= 604800; part += 1; }
|
||||||
|
return part + " weeks ago";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ago < 5184000) { return "a month ago"; }
|
||||||
|
if (ago < 31536001) {
|
||||||
|
while (ago >= 2592000) { ago -= 2592000; part += 1; }
|
||||||
|
return part + " months ago";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ago < 315360000) { // 10 years
|
||||||
|
return "more than year ago";
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO never
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
}('undefined' !== typeof module ? module.exports : window));
|
||||||
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Devices = module.exports;
|
var Devices = module.exports;
|
||||||
|
// TODO enumerate store's keys and device's keys for documentation
|
||||||
Devices.addPort = function (store, serverport, newDevice) {
|
Devices.addPort = function (store, serverport, newDevice) {
|
||||||
// TODO make special
|
// TODO make special
|
||||||
return Devices.add(store, serverport, newDevice, true);
|
return Devices.add(store, serverport, newDevice, true);
|
||||||
@ -14,6 +15,7 @@ Devices.add = function (store, servername, newDevice, isPort) {
|
|||||||
if (!store._domains) { store._domains = {}; }
|
if (!store._domains) { store._domains = {}; }
|
||||||
if (!store._domains[servername]) { store._domains[servername] = []; }
|
if (!store._domains[servername]) { store._domains[servername] = []; }
|
||||||
store._domains[servername].push(newDevice);
|
store._domains[servername].push(newDevice);
|
||||||
|
Devices.touch(store, servername);
|
||||||
|
|
||||||
// add device
|
// add device
|
||||||
// TODO only use a device id
|
// TODO only use a device id
|
||||||
@ -126,7 +128,11 @@ Devices.active = function (store, id) {
|
|||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
Devices.exist = function (store, servername) {
|
Devices.exist = function (store, servername) {
|
||||||
return !!(Devices.list(store, servername).length);
|
if (Devices.list(store, servername).length) {
|
||||||
|
Devices.touch(store, servername);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
Devices.next = function (store, servername) {
|
Devices.next = function (store, servername) {
|
||||||
var devices = Devices.list(store, servername);
|
var devices = Devices.list(store, servername);
|
||||||
@ -138,5 +144,20 @@ Devices.next = function (store, servername) {
|
|||||||
device = devices[devices._index || 0];
|
device = devices[devices._index || 0];
|
||||||
devices._index = (devices._index || 0) + 1;
|
devices._index = (devices._index || 0) + 1;
|
||||||
|
|
||||||
|
if (device) { Devices.touch(store, servername); }
|
||||||
return device;
|
return device;
|
||||||
};
|
};
|
||||||
|
Devices.touchDevice = function (store, device) {
|
||||||
|
// TODO use device.id (which will be pubkey thumbprint) and store._devices[id].domainsMap
|
||||||
|
Object.keys(device.domainsMap).forEach(function (servername) {
|
||||||
|
Devices.touch(store, servername);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Devices.touch = function (store, servername) {
|
||||||
|
if (!store._recency) { store._recency = {}; }
|
||||||
|
store._recency[servername] = Date.now();
|
||||||
|
};
|
||||||
|
Devices.lastSeen = function (store, servername) {
|
||||||
|
if (!store._recency) { store._recency = {}; }
|
||||||
|
return store._recency[servername] || 0;
|
||||||
|
};
|
||||||
|
|||||||
@ -10,7 +10,7 @@ function noSniCallback(tag) {
|
|||||||
var err = new Error("[noSniCallback] no handler set for '" + tag + "':'" + servername + "'");
|
var err = new Error("[noSniCallback] no handler set for '" + tag + "':'" + servername + "'");
|
||||||
console.error(err.message);
|
console.error(err.message);
|
||||||
cb(new Error(err));
|
cb(new Error(err));
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.create = function (state) {
|
module.exports.create = function (state) {
|
||||||
@ -72,38 +72,44 @@ module.exports.create = function (state) {
|
|||||||
state.tlsInvalidSniServer.on('tlsClientError', function () {
|
state.tlsInvalidSniServer.on('tlsClientError', function () {
|
||||||
console.error('tlsClientError InvalidSniServer');
|
console.error('tlsClientError InvalidSniServer');
|
||||||
});
|
});
|
||||||
state.httpsInvalid = function (servername, socket) {
|
state.createHttpInvalid = function (opts) {
|
||||||
|
return http.createServer(function (req, res) {
|
||||||
|
if (!opts.servername) {
|
||||||
|
res.statusCode = 422;
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||||
|
res.end(
|
||||||
|
"3. An inexplicable temporal shift of the quantum realm... that makes me feel uncomfortable.\n\n"
|
||||||
|
+ "[ERROR] No SNI header was sent. I can only think of two possible explanations for this:\n"
|
||||||
|
+ "\t1. You really love Windows XP and you just won't let go of Internet Explorer 6\n"
|
||||||
|
+ "\t2. You're writing a bot and you forgot to set the servername parameter\n"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use req.headers.host instead of servername (since domain fronting is disabled anyway)
|
||||||
|
res.statusCode = 502;
|
||||||
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||||
|
res.end(
|
||||||
|
"<h1>Oops!</h1>"
|
||||||
|
+ "<p>It looks like '" + encodeURIComponent(opts.servername) + "' isn't connected right now.</p>"
|
||||||
|
+ "<p><small>Last seen: " + opts.ago + "</small></p>"
|
||||||
|
+ "<p><small>Error: 502 Bad Gateway</small></p>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
state.httpsInvalid = function (opts, socket) {
|
||||||
// none of these methods work:
|
// none of these methods work:
|
||||||
// httpsServer.emit('connection', socket); // this didn't work
|
// httpsServer.emit('connection', socket); // this didn't work
|
||||||
// tlsServer.emit('connection', socket); // this didn't work either
|
// tlsServer.emit('connection', socket); // this didn't work either
|
||||||
//console.log('chunkLen', firstChunk.byteLength);
|
//console.log('chunkLen', firstChunk.byteLength);
|
||||||
|
|
||||||
console.log('[httpsInvalid] servername', servername);
|
console.log('[httpsInvalid] servername', opts.servername);
|
||||||
//state.tlsInvalidSniServer.emit('connection', wrapSocket(socket));
|
//state.tlsInvalidSniServer.emit('connection', wrapSocket(socket));
|
||||||
var tlsInvalidSniServer = tls.createServer(state.tlsOptions, function (tlsSocket) {
|
var tlsInvalidSniServer = tls.createServer(state.tlsOptions, function (tlsSocket) {
|
||||||
console.log('[tlsInvalid] tls connection');
|
console.log('[tlsInvalid] tls connection');
|
||||||
// things get a little messed up here
|
// We create an entire http server object because it's difficult to figure out
|
||||||
var httpInvalidSniServer = http.createServer(function (req, res) {
|
// how to access the original tlsSocket to get the servername
|
||||||
if (!servername) {
|
state.createHttpInvalid(opts).emit('connection', tlsSocket);
|
||||||
res.statusCode = 422;
|
|
||||||
res.end(
|
|
||||||
"3. An inexplicable temporal shift of the quantum realm... that makes me feel uncomfortable.\n\n"
|
|
||||||
+ "[ERROR] No SNI header was sent. I can only think of two possible explanations for this:\n"
|
|
||||||
+ "\t1. You really love Windows XP and you just won't let go of Internet Explorer 6\n"
|
|
||||||
+ "\t2. You're writing a bot and you forgot to set the servername parameter\n"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.end(
|
|
||||||
"You came in hot looking for '" + servername + "' and, granted, the IP address for that domain"
|
|
||||||
+ " must be pointing here (or else how could you be here?), nevertheless either it's not registered"
|
|
||||||
+ " in the internal system at all (which Seth says isn't even a thing) or there is no device"
|
|
||||||
+ " connected on the south side of the network which has informed me that it's ready to have traffic"
|
|
||||||
+ " for that domain forwarded to it (sorry I didn't check that deeply to determine which).\n\n"
|
|
||||||
+ "Either way, you're doing strange things that make me feel uncomfortable... Please don't touch me there any more.");
|
|
||||||
});
|
|
||||||
httpInvalidSniServer.emit('connection', tlsSocket);
|
|
||||||
});
|
});
|
||||||
tlsInvalidSniServer.on('tlsClientError', function () {
|
tlsInvalidSniServer.on('tlsClientError', function () {
|
||||||
console.error('tlsClientError InvalidSniServer httpsInvalid');
|
console.error('tlsClientError InvalidSniServer httpsInvalid');
|
||||||
|
|||||||
@ -172,6 +172,7 @@ var Server = {
|
|||||||
, _initSocketHandlers: function (state, srv) {
|
, _initSocketHandlers: function (state, srv) {
|
||||||
function refreshTimeout() {
|
function refreshTimeout() {
|
||||||
srv.lastActivity = Date.now();
|
srv.lastActivity = Date.now();
|
||||||
|
Devices.touchDevice(state.deviceLists, srv);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkTimeout() {
|
function checkTimeout() {
|
||||||
|
|||||||
@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
var sni = require('sni');
|
var sni = require('sni');
|
||||||
var pipeWs = require('./pipe-ws.js');
|
var pipeWs = require('./pipe-ws.js');
|
||||||
|
var ago = require('./ago.js').AGO;
|
||||||
|
var up = Date.now();
|
||||||
|
|
||||||
|
function fromUptime(ms) {
|
||||||
|
if (ms) {
|
||||||
|
return ago(Date.now() - ms);
|
||||||
|
} else {
|
||||||
|
return "Not seen since relay restarted, " + ago(Date.now() - up);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.createTcpConnectionHandler = function (state) {
|
module.exports.createTcpConnectionHandler = function (state) {
|
||||||
var Devices = state.Devices;
|
var Devices = state.Devices;
|
||||||
@ -27,6 +37,16 @@ module.exports.createTcpConnectionHandler = function (state) {
|
|||||||
var str;
|
var str;
|
||||||
var m;
|
var m;
|
||||||
|
|
||||||
|
if (!firstChunk) {
|
||||||
|
try {
|
||||||
|
conn.end();
|
||||||
|
} catch(e) {
|
||||||
|
console.error("[lib/unwrap-tls.js] Error:", e);
|
||||||
|
conn.destroy();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//conn.pause();
|
//conn.pause();
|
||||||
conn.unshift(firstChunk);
|
conn.unshift(firstChunk);
|
||||||
|
|
||||||
@ -38,8 +58,15 @@ module.exports.createTcpConnectionHandler = function (state) {
|
|||||||
|
|
||||||
// defer after return (instead of being in many places)
|
// defer after return (instead of being in many places)
|
||||||
function deferData(fn) {
|
function deferData(fn) {
|
||||||
if (fn) {
|
if ('httpsInvalid' === fn) {
|
||||||
|
state[fn]({
|
||||||
|
servername: servername
|
||||||
|
, ago: fromUptime(Devices.lastSeen(state.deviceLists, servername))
|
||||||
|
}, conn);
|
||||||
|
} else if (fn) {
|
||||||
state[fn](servername, conn);
|
state[fn](servername, conn);
|
||||||
|
} else {
|
||||||
|
console.error("[SANITY ERROR] '" + fn + "' doesn't have a state handler");
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
@ -48,33 +75,81 @@ module.exports.createTcpConnectionHandler = function (state) {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryTls() {
|
var httpOutcomes = {
|
||||||
var vhost;
|
missingServername: function () {
|
||||||
|
console.log("[debug] [http] missing servername");
|
||||||
if (!state.servernames.length) {
|
// TODO use a more specific error page
|
||||||
console.info("[Setup] https => admin => setup => (needs bogus tls certs to start?)");
|
deferData('handleInsecureHttp');
|
||||||
deferData('httpsSetupServer');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
, requiresSetup: function () {
|
||||||
if (-1 !== state.servernames.indexOf(servername)) {
|
console.log("[debug] [http] requires setup");
|
||||||
if (state.debug) { console.log("[Admin]", servername); }
|
// TODO Insecure connections for setup will not work on secure domains (i.e. .app)
|
||||||
deferData('httpsTunnel');
|
state.httpSetupServer.emit('connection', conn);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
, isInternal: function () {
|
||||||
if (state.config.nowww && /^www\./i.test(servername)) {
|
console.log("[debug] [http] is known internally (admin)");
|
||||||
console.log("TODO: use www bare redirect");
|
if (/well-known/.test(str)) {
|
||||||
|
deferData('handleHttp');
|
||||||
|
} else {
|
||||||
|
deferData('handleInsecureHttp');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
, isVhost: function () {
|
||||||
|
console.log("[debug] [http] is vhost (normal server)");
|
||||||
|
if (/well-known/.test(str)) {
|
||||||
|
deferData('handleHttp');
|
||||||
|
} else {
|
||||||
|
deferData('handleInsecureHttp');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, assumeExternal: function () {
|
||||||
|
console.log("[debug] [http] assume external");
|
||||||
|
var service = 'http';
|
||||||
|
|
||||||
if (!servername) {
|
if (!Devices.exist(state.deviceLists, servername)) {
|
||||||
|
// It would be better to just re-read the host header rather
|
||||||
|
// than creating a whole server object, but this is a "rare"
|
||||||
|
// case and I'm feeling lazy right now.
|
||||||
|
console.log("[debug] [http] no device connected");
|
||||||
|
state.createHttpInvalid({
|
||||||
|
servername: servername
|
||||||
|
, ago: fromUptime(Devices.lastSeen(state.deviceLists, servername))
|
||||||
|
}).emit('connection', conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO make https redirect configurable on a per-domain basis
|
||||||
|
// /^\/\.well-known\/acme-challenge\//.test(str)
|
||||||
|
if (/well-known/.test(str)) {
|
||||||
|
// HTTP
|
||||||
|
console.log("[debug] [http] passthru");
|
||||||
|
pipeWs(servername, service, Devices.next(state.deviceLists, servername), conn, serviceport);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log("[debug] [http] redirect to https");
|
||||||
|
deferData('handleInsecureHttp');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var tlsOutcomes = {
|
||||||
|
missingServername: function () {
|
||||||
if (state.debug) { console.log("No SNI was given, so there's nothing we can do here"); }
|
if (state.debug) { console.log("No SNI was given, so there's nothing we can do here"); }
|
||||||
deferData('httpsInvalid');
|
deferData('httpsInvalid');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
, requiresSetup: function () {
|
||||||
function run() {
|
console.info("[Setup] https => admin => setup => (needs bogus tls certs to start?)");
|
||||||
var nextDevice = Devices.next(state.deviceLists, servername);
|
deferData('httpsSetupServer');
|
||||||
|
}
|
||||||
|
, isInternal: function () {
|
||||||
|
if (state.debug) { console.log("[Admin]", servername); }
|
||||||
|
deferData('httpsTunnel');
|
||||||
|
}
|
||||||
|
, isVhost: function (vhost) {
|
||||||
|
if (state.debug) { console.log("[tcp] [vhost]", state.config.vhost, "=>", vhost); }
|
||||||
|
deferData('httpsVhost');
|
||||||
|
}
|
||||||
|
, assumeExternal: function () {
|
||||||
|
var nextDevice = Devices.next(state.deviceLists, servername);
|
||||||
if (!nextDevice) {
|
if (!nextDevice) {
|
||||||
if (state.debug) { console.log("No devices match the given servername"); }
|
if (state.debug) { console.log("No devices match the given servername"); }
|
||||||
deferData('httpsInvalid');
|
deferData('httpsInvalid');
|
||||||
@ -82,27 +157,33 @@ module.exports.createTcpConnectionHandler = function (state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state.debug) { console.log("pipeWs(servername, service, deviceLists['" + servername + "'], socket)"); }
|
if (state.debug) { console.log("pipeWs(servername, service, deviceLists['" + servername + "'], socket)"); }
|
||||||
deferData();
|
|
||||||
pipeWs(servername, service, nextDevice, conn, serviceport);
|
pipeWs(servername, service, nextDevice, conn, serviceport);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleConnection(outcomes) {
|
||||||
|
var vhost;
|
||||||
|
|
||||||
|
// No routing information available
|
||||||
|
if (!servername) { outcomes.missingServername(); return; }
|
||||||
|
// Server needs to be set up
|
||||||
|
if (!state.servernames.length) { outcomes.requiresSetup(); return; }
|
||||||
|
// This is one of the admin domains
|
||||||
|
if (-1 !== state.servernames.indexOf(servername)) { outcomes.isInternal(); return; }
|
||||||
|
|
||||||
// TODO don't run an fs check if we already know this is working elsewhere
|
// TODO don't run an fs check if we already know this is working elsewhere
|
||||||
//if (!state.validHosts) { state.validHosts = {}; }
|
//if (!state.validHosts) { state.validHosts = {}; }
|
||||||
if (state.config.vhost) {
|
if (state.config.vhost) {
|
||||||
vhost = state.config.vhost.replace(/:hostname/, (servername||'reallydoesntexist'));
|
vhost = state.config.vhost.replace(/:hostname/, servername);
|
||||||
if (state.debug) { console.log("[tcp] [vhost]", state.config.vhost, "=>", vhost); }
|
|
||||||
//state.httpsVhost(servername, conn);
|
|
||||||
//return;
|
|
||||||
require('fs').readdir(vhost, function (err, nodes) {
|
require('fs').readdir(vhost, function (err, nodes) {
|
||||||
if (state.debug && err) { console.log("VHOST error", err); }
|
if (state.debug && err) { console.log("VHOST error", err); }
|
||||||
if (err || !nodes) { run(); return; }
|
if (err || !nodes) { outcomes.assumeExternal(); return; }
|
||||||
//if (nodes) { deferData('httpsVhost'); return; }
|
outcomes.isVhost(vhost);
|
||||||
deferData('httpsVhost');
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
outcomes.assumeExternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
|
// https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
|
||||||
@ -111,40 +192,19 @@ module.exports.createTcpConnectionHandler = function (state) {
|
|||||||
service = 'https';
|
service = 'https';
|
||||||
servername = (sni(firstChunk)||'').toLowerCase().trim();
|
servername = (sni(firstChunk)||'').toLowerCase().trim();
|
||||||
if (state.debug) { console.log("[tcp] tls hello from '" + servername + "'"); }
|
if (state.debug) { console.log("[tcp] tls hello from '" + servername + "'"); }
|
||||||
tryTls();
|
handleConnection(tlsOutcomes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstChunk[0] > 32 && firstChunk[0] < 127) {
|
if (firstChunk[0] > 32 && firstChunk[0] < 127) {
|
||||||
|
// (probably) HTTP
|
||||||
str = firstChunk.toString();
|
str = firstChunk.toString();
|
||||||
m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
|
m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
|
||||||
servername = (m && m[1].toLowerCase() || '').split(':')[0];
|
servername = (m && m[1].toLowerCase() || '').split(':')[0];
|
||||||
if (state.debug) { console.log("[tcp] http hostname '" + servername + "'"); }
|
if (state.debug) { console.log("[tcp] http hostname '" + servername + "'"); }
|
||||||
|
|
||||||
if (/HTTP\//i.test(str)) {
|
if (/HTTP\//i.test(str)) {
|
||||||
if (!state.servernames.length) {
|
handleConnection(httpOutcomes);
|
||||||
console.info("[tcp] No admin servername. Entering setup mode.");
|
|
||||||
deferData();
|
|
||||||
state.httpSetupServer.emit('connection', conn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
service = 'http';
|
|
||||||
// TODO make https redirect configurable
|
|
||||||
// /^\/\.well-known\/acme-challenge\//.test(str)
|
|
||||||
if (/well-known/.test(str)) {
|
|
||||||
// HTTP
|
|
||||||
if (Devices.exist(state.deviceLists, servername)) {
|
|
||||||
deferData();
|
|
||||||
pipeWs(servername, service, Devices.next(state.deviceLists, servername), conn, serviceport);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
deferData('handleHttp');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// redirect to https
|
|
||||||
deferData('handleInsecureHttp');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
snap/snapcraft.yaml
Normal file
24
snap/snapcraft.yaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
name: telebit-relay
|
||||||
|
version: '0.20.0'
|
||||||
|
summary: Because friends don't let friends localhost
|
||||||
|
description: |
|
||||||
|
A server that works in combination with Telebit Remote
|
||||||
|
to allow you to serve http and https from any computer,
|
||||||
|
anywhere through a secure tunnel.
|
||||||
|
|
||||||
|
grade: stable
|
||||||
|
confinement: strict
|
||||||
|
|
||||||
|
apps:
|
||||||
|
telebit-relay:
|
||||||
|
command: telebit-relay --config $SNAP_COMMON/config.yml
|
||||||
|
plugs: [network, network-bind]
|
||||||
|
daemon: simple
|
||||||
|
|
||||||
|
parts:
|
||||||
|
telebit-relay:
|
||||||
|
plugin: nodejs
|
||||||
|
node-engine: 10.13.0
|
||||||
|
source: .
|
||||||
|
override-build: |
|
||||||
|
snapcraftctl build
|
||||||
Loading…
x
Reference in New Issue
Block a user