WIP get preauth clientside and realauth daemonside
This commit is contained in:
parent
02a89b52a4
commit
9441bf7f8e
561
bin/telebit.js
561
bin/telebit.js
|
@ -35,8 +35,8 @@ if (-1 !== argIndex) {
|
|||
}
|
||||
|
||||
function help() {
|
||||
console.info('');
|
||||
console.info('Telebit Remote v' + pkg.version);
|
||||
//console.info('');
|
||||
//console.info('Telebit Remote v' + pkg.version);
|
||||
console.info('');
|
||||
console.info('Usage:');
|
||||
console.info('');
|
||||
|
@ -90,8 +90,7 @@ if (!confpath || /^--/.test(confpath)) {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
function askForConfig(answers, mainCb) {
|
||||
answers = answers || {};
|
||||
function askForConfig(state, mainCb) {
|
||||
var fs = require('fs');
|
||||
var ttyname = '/dev/tty';
|
||||
var stdin = useTty ? fs.createReadStream(ttyname, {
|
||||
|
@ -104,7 +103,7 @@ function askForConfig(answers, mainCb) {
|
|||
// https://github.com/nodejs/node/issues/21319
|
||||
, terminal: !useTty
|
||||
});
|
||||
answers._useTty = useTty;
|
||||
state._useTty = useTty;
|
||||
|
||||
// NOTE: Use of setTimeout
|
||||
// We're using setTimeout just to make the user experience a little
|
||||
|
@ -115,7 +114,7 @@ function askForConfig(answers, mainCb) {
|
|||
// ~ 150-250ms is the sweet spot for most humans (long enough to notice change and not be jarred, but stay on task)
|
||||
var firstSet = [
|
||||
function askEmail(cb) {
|
||||
if (answers.email) { cb(); return; }
|
||||
if (state.config.email) { cb(); return; }
|
||||
//console.info("");
|
||||
console.info("Welcome!");
|
||||
console.info("");
|
||||
|
@ -130,8 +129,8 @@ function askForConfig(answers, mainCb) {
|
|||
rl.question('email: ', function (email) {
|
||||
email = /@/.test(email) && email.trim();
|
||||
if (!email) { askEmail(cb); return; }
|
||||
answers.email = email.trim();
|
||||
answers.agree_tos = true;
|
||||
state.config.email = email.trim();
|
||||
state.config.agreeTos = true;
|
||||
console.info("");
|
||||
setTimeout(cb, 250);
|
||||
});
|
||||
|
@ -159,14 +158,14 @@ function askForConfig(answers, mainCb) {
|
|||
console.warn("");
|
||||
console.warn(body);
|
||||
} else if (body && body.pair_request) {
|
||||
answers._can_pair = true;
|
||||
state._can_pair = true;
|
||||
}
|
||||
answers.relay = relay;
|
||||
state.config.relay = relay;
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
if (answers.relay) { checkRelay(); return; }
|
||||
if (state.config.relay) { checkRelay(); return; }
|
||||
console.info("");
|
||||
console.info("");
|
||||
console.info("What relay will you be using? (press enter for default)");
|
||||
|
@ -175,10 +174,10 @@ function askForConfig(answers, mainCb) {
|
|||
}
|
||||
, function checkRelay(cb) {
|
||||
nextSet = [];
|
||||
if ('telebit.cloud' !== answers.relay) {
|
||||
if ('telebit.cloud' !== state.config.relay) {
|
||||
nextSet = nextSet.concat(standardSet);
|
||||
}
|
||||
if (!answers._can_pair) {
|
||||
if (!state._can_pair) {
|
||||
nextSet = nextSet.concat(fossSet);
|
||||
}
|
||||
cb();
|
||||
|
@ -188,7 +187,7 @@ function askForConfig(answers, mainCb) {
|
|||
// There are questions that we need to aks in the CLI
|
||||
// if we can't guarantee that they are being asked in the web interface
|
||||
function askAgree(cb) {
|
||||
if (answers.agree_tos) { cb(); return; }
|
||||
if (state.config.agreeTos) { cb(); return; }
|
||||
console.info("");
|
||||
console.info("");
|
||||
console.info("Do you accept the terms of service for each and all of the following?");
|
||||
|
@ -204,7 +203,7 @@ function askForConfig(answers, mainCb) {
|
|||
if (!/^y(es)?$/i.test(resp) && 'true' !== resp) {
|
||||
throw new Error("You didn't accept the Terms of Service... not sure what to do...");
|
||||
}
|
||||
answers.agree_tos = true;
|
||||
state.config.agreeTos = true;
|
||||
console.info("");
|
||||
setTimeout(cb, 250);
|
||||
});
|
||||
|
@ -212,35 +211,35 @@ function askForConfig(answers, mainCb) {
|
|||
, function askUpdates(cb) {
|
||||
// required means transactional, security alerts, mandatory updates
|
||||
var options = [ 'newsletter', 'important', 'required' ];
|
||||
if (-1 !== options.indexOf(answers.updates)) { cb(); return; }
|
||||
if (-1 !== options.indexOf(state._updates)) { cb(); return; }
|
||||
console.info("");
|
||||
console.info("");
|
||||
console.info("What updates would you like to receive? (" + options.join(',') + ")");
|
||||
console.info("");
|
||||
rl.question('messages (default: important): ', function (updates) {
|
||||
updates = (updates || '').trim().toLowerCase();
|
||||
if (!updates) { updates = 'important'; }
|
||||
if (-1 === options.indexOf(updates)) { askUpdates(cb); return; }
|
||||
state._updates = (updates || '').trim().toLowerCase();
|
||||
if (!state._updates) { state._updates = 'important'; }
|
||||
if (-1 === options.indexOf(state._updates)) { askUpdates(cb); return; }
|
||||
|
||||
if ('newsletter' === updates) {
|
||||
answers.newsletter = true;
|
||||
answers.communityMember = true;
|
||||
} else if ('important' === updates) {
|
||||
answers.communityMember = true;
|
||||
if ('newsletter' === state._updates) {
|
||||
state.config.newsletter = true;
|
||||
state.config.communityMember = true;
|
||||
} else if ('important' === state._updates) {
|
||||
state.config.communityMember = true;
|
||||
}
|
||||
|
||||
setTimeout(cb, 250);
|
||||
});
|
||||
}
|
||||
, function askTelemetry(cb) {
|
||||
if (answers.telemetry) { cb(); return; }
|
||||
if (state.config.telemetry) { cb(); return; }
|
||||
console.info("");
|
||||
console.info("");
|
||||
console.info("Contribute project telemetry data? (press enter for default [yes])");
|
||||
console.info("");
|
||||
rl.question('telemetry [Y/n]: ', function (telemetry) {
|
||||
if (!telemetry || /^y(es)?$/i.test(telemetry)) {
|
||||
answers.telemetry = true;
|
||||
state.config.telemetry = true;
|
||||
}
|
||||
setTimeout(cb, 250);
|
||||
});
|
||||
|
@ -248,10 +247,11 @@ function askForConfig(answers, mainCb) {
|
|||
];
|
||||
var fossSet = [
|
||||
function askTokenOrSecret(cb) {
|
||||
if (answers._can_pair || answers.token || answers.secret) { cb(); return; }
|
||||
if (state._can_pair || state.token || state.config.token
|
||||
|| state.secret || state.config.secret) { cb(); return; }
|
||||
console.info("");
|
||||
console.info("");
|
||||
console.info("What's your authorization for '" + answers.relay + "'?");
|
||||
console.info("What's your authorization for '" + state.config.relay + "'?");
|
||||
console.info("");
|
||||
// TODO check .well-known to learn supported token types
|
||||
console.info("Currently supported:");
|
||||
|
@ -264,24 +264,17 @@ function askForConfig(answers, mainCb) {
|
|||
var jwt = require('jsonwebtoken');
|
||||
resp = (resp || '').trim();
|
||||
try {
|
||||
answers.token = jwt.decode(resp);
|
||||
state.config.token = jwt.decode(resp);
|
||||
} catch(e) {
|
||||
// is not jwt
|
||||
try {
|
||||
if (JSON.parse(resp).subject) {
|
||||
answers.token = resp;
|
||||
}
|
||||
} catch(e) {
|
||||
// is not authRequest either
|
||||
}
|
||||
}
|
||||
if (!answers.token) {
|
||||
if (!state.config.token) {
|
||||
resp = resp.toLowerCase();
|
||||
if (resp === Buffer.from(resp, 'hex').toString('hex')) {
|
||||
answers.secret = resp;
|
||||
state.config.secret = resp;
|
||||
}
|
||||
}
|
||||
if (!answers.token && !answers.secret) {
|
||||
if (!state.config.token && !state.config.secret) {
|
||||
askTokenOrSecret(cb);
|
||||
return;
|
||||
}
|
||||
|
@ -289,7 +282,7 @@ function askForConfig(answers, mainCb) {
|
|||
});
|
||||
}
|
||||
, function askServernames(cb) {
|
||||
if (!answers.secret || answers.servernames) { cb(); return; }
|
||||
if (!state.config.secret || state.config._servernames) { cb(); return; }
|
||||
console.info("");
|
||||
console.info("");
|
||||
console.info("What servername(s) will you be relaying here?");
|
||||
|
@ -299,12 +292,12 @@ function askForConfig(answers, mainCb) {
|
|||
resp = (resp || '').trim().split(/,/g);
|
||||
if (!resp.length) { askServernames(); return; }
|
||||
// TODO validate the domains
|
||||
answers.servernames = resp;
|
||||
state.config._servernames = resp;
|
||||
setTimeout(cb, 250);
|
||||
});
|
||||
}
|
||||
, function askPorts(cb) {
|
||||
if (!answers.secret || answers.ports) { cb(); return; }
|
||||
if (!state.config.secret || state.config._ports) { cb(); return; }
|
||||
console.info("");
|
||||
console.info("");
|
||||
console.info("What tcp port(s) will you be relaying here?");
|
||||
|
@ -314,7 +307,7 @@ function askForConfig(answers, mainCb) {
|
|||
resp = (resp || '').trim().split(/,/g);
|
||||
if (!resp.length) { askPorts(); return; }
|
||||
// TODO validate the domains
|
||||
answers.ports = resp;
|
||||
state.config._ports = resp;
|
||||
setTimeout(cb, 250);
|
||||
});
|
||||
}
|
||||
|
@ -328,7 +321,7 @@ function askForConfig(answers, mainCb) {
|
|||
if (useTty) { try { stdin.push(null); } catch(e) { /*ignore*/ } }
|
||||
rl.close();
|
||||
if (useTty) { try { stdin.close(); } catch(e) { /*ignore*/ } }
|
||||
mainCb(null, answers);
|
||||
mainCb(null, state);
|
||||
return;
|
||||
}
|
||||
q(next);
|
||||
|
@ -403,6 +396,8 @@ var utils = {
|
|||
});
|
||||
}
|
||||
, putConfig: function putConfig(service, args, fn) {
|
||||
//console.log('debug path:');
|
||||
//console.log('/rpc/' + service + '?_body=' + JSON.stringify(args));
|
||||
var req = http.get({
|
||||
socketPath: state._ipc.path
|
||||
, method: 'POST'
|
||||
|
@ -465,53 +460,143 @@ var utils = {
|
|||
}
|
||||
};
|
||||
|
||||
function parseConfig(err, text) {
|
||||
// Two styles:
|
||||
// http 3000
|
||||
// http modulename
|
||||
function makeRpc(key) {
|
||||
if (key !== argv[0]) {
|
||||
return false;
|
||||
}
|
||||
utils.putConfig(argv[0], argv.slice(1));
|
||||
return true;
|
||||
}
|
||||
|
||||
console.info("");
|
||||
console.info(verstr.join(' '));
|
||||
function packConfig(config) {
|
||||
return Object.keys(config).map(function (key) {
|
||||
var val = config[key];
|
||||
if (val && 'object' === typeof val && !Array.isArray(val)) {
|
||||
val = JSON.stringify(val);
|
||||
}
|
||||
return key + ':' + val; // converts arrays to strings with ,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
state.config = JSON.parse(text || '{}');
|
||||
} catch(e1) {
|
||||
try {
|
||||
state.config = YAML.safeLoad(text || '{}');
|
||||
} catch(e2) {
|
||||
console.error(e1.message);
|
||||
console.error(e2.message);
|
||||
process.exit(1);
|
||||
function getToken(err, state) {
|
||||
if (err) {
|
||||
console.error("Error while initializing config [init]:");
|
||||
throw err;
|
||||
}
|
||||
|
||||
// { _otp, config: {} }
|
||||
common.api.token(state, {
|
||||
error: function (err/*, next*/) {
|
||||
console.error("[Error] common.api.token:");
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state.config = camelCopy(state.config || {}) || {};
|
||||
common._init(
|
||||
state.config.root || path.join(os.homedir(), '.local/share/telebit')
|
||||
, (state.config.root && path.join(state.config.root, 'etc')) || path.join(os.homedir(), '.config/telebit')
|
||||
);
|
||||
state._ipc = common.pipename(state.config, true);
|
||||
|
||||
if (!Object.keys(state.config).length) {
|
||||
console.info('(' + state._ipc.comment + ": " + state._ipc.path + ')');
|
||||
}
|
||||
console.info("");
|
||||
|
||||
if ((err && 'ENOENT' === err.code) || !Object.keys(state.config).length) {
|
||||
if (!err || 'ENOENT' === err.code) {
|
||||
//console.warn("Empty config file. Run 'telebit init' to configure.\n");
|
||||
} else {
|
||||
console.warn("Couldn't load config:\n\n\t" + err.message + "\n");
|
||||
, directory: function (dir, next) {
|
||||
//console.log('Telebit Relay Discovered:');
|
||||
state._apiDirectory = dir;
|
||||
//console.log(dir);
|
||||
//console.log();
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
// Two styles:
|
||||
// http 3000
|
||||
// http modulename
|
||||
function makeRpc(key) {
|
||||
if (key !== argv[0]) {
|
||||
return false;
|
||||
, tunnelUrl: function (tunnelUrl, next) {
|
||||
//console.log('Telebit Relay Tunnel Socket:', tunnelUrl);
|
||||
state.wss = tunnelUrl;
|
||||
next();
|
||||
}
|
||||
utils.putConfig(argv[0], argv.slice(1));
|
||||
return true;
|
||||
, requested: function (authReq, next) {
|
||||
//console.log("Pairing Requested");
|
||||
state.config._otp = state.config._otp = authReq.otp;
|
||||
|
||||
if (!state.config.token && state._can_pair) {
|
||||
console.info("");
|
||||
console.info("==============================================");
|
||||
console.info(" Hey, Listen! ");
|
||||
console.info("==============================================");
|
||||
console.info(" ");
|
||||
console.info(" GO CHECK YOUR EMAIL! ");
|
||||
console.info(" ");
|
||||
console.info(" DEVICE PAIR CODE: 0000 ".replace(/0000/g, state.config._otp));
|
||||
console.info(" ");
|
||||
console.info("==============================================");
|
||||
console.info("");
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
, connect: function (pretoken, next) {
|
||||
//console.log("Enabling Pairing Locally...");
|
||||
state.config.pretoken = pretoken;
|
||||
state._connecting = true;
|
||||
|
||||
// TODO use php-style object querification
|
||||
// TODO XXX
|
||||
utils.putConfig('config', packConfig(state.config), function (err/*, body*/) {
|
||||
if (err) {
|
||||
state._error = err;
|
||||
console.error("Error while initializing config [connect]:");
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
console.log("waiting...");
|
||||
next();
|
||||
});
|
||||
}
|
||||
, offer: function (token, next) {
|
||||
//console.log("Pairing Enabled by Relay");
|
||||
state.config.token = token;
|
||||
if (state._error) {
|
||||
return;
|
||||
}
|
||||
if (state._connecting) {
|
||||
return;
|
||||
}
|
||||
state._connecting = true;
|
||||
utils.putConfig('config', packConfig(state.config), function (err/*, body*/) {
|
||||
if (err) {
|
||||
state._error = err;
|
||||
console.error("Error while initializing config [offer]:");
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
//console.log("Pairing Enabled Locally");
|
||||
next();
|
||||
});
|
||||
}
|
||||
, granted: function (_, next) {
|
||||
//console.log("Pairing complete!");
|
||||
next();
|
||||
}
|
||||
, end: function () {
|
||||
utils.putConfig('enable', [], function (err) {
|
||||
if (err) { console.error(err); return; }
|
||||
//console.info("Success");
|
||||
|
||||
// workaround for https://github.com/nodejs/node/issues/21319
|
||||
if (state._useTty) {
|
||||
setTimeout(function () {
|
||||
console.info();
|
||||
console.info("Press any key to continue...");
|
||||
console.info();
|
||||
process.exit(0);
|
||||
}, 0.5 * 1000);
|
||||
return;
|
||||
}
|
||||
// end workaround
|
||||
|
||||
parseCli(state);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function parseCli(/*state*/) {
|
||||
if (-1 !== argv.indexOf('init')) {
|
||||
utils.putConfig('list', []/*, function (err) {
|
||||
}*/);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([ 'ssh', 'http', 'tcp' ].some(function (key) {
|
||||
|
@ -522,124 +607,10 @@ function parseConfig(err, text) {
|
|||
utils.putConfig(argv[0], argv.slice(1));
|
||||
return true;
|
||||
}
|
||||
help();
|
||||
return true;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (-1 !== argv.indexOf('init')) {
|
||||
parsers.init(argv, function (err, answers) {
|
||||
if (err) {
|
||||
console.error("Error while initializing config [init]:");
|
||||
throw err;
|
||||
}
|
||||
|
||||
common.api.token(state, {
|
||||
error: function (err/*, next*/) {
|
||||
console.error("[Error] common.api.token:");
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
, directory: function (dir, next) {
|
||||
//console.log('Telebit Relay Discovered:');
|
||||
state._apiDirectory = dir;
|
||||
//console.log(dir);
|
||||
//console.log();
|
||||
next();
|
||||
}
|
||||
, tunnelUrl: function (tunnelUrl, next) {
|
||||
//console.log('Telebit Relay Tunnel Socket:', tunnelUrl);
|
||||
state.wss = tunnelUrl;
|
||||
next();
|
||||
}
|
||||
, requested: function (authReq, next) {
|
||||
//console.log("Pairing Requested");
|
||||
var pin = authReq.pin || authReq.otp || authReq.pairCode;
|
||||
state.otp = state._otp = pin;
|
||||
state.auth = state.authRequest = state._auth = authReq;
|
||||
|
||||
if (!answers.token && answers._can_pair) {
|
||||
console.info("");
|
||||
console.info("==============================================");
|
||||
console.info(" Hey, Listen! ");
|
||||
console.info("==============================================");
|
||||
console.info(" ");
|
||||
console.info(" GO CHECK YOUR EMAIL! ");
|
||||
console.info(" ");
|
||||
console.info(" DEVICE PAIR CODE: 0000 ".replace(/0000/g, answers._otp));
|
||||
console.info(" ");
|
||||
console.info("==============================================");
|
||||
console.info("");
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
, connect: function (pretoken, next) {
|
||||
//console.log("Enabling Pairing Locally...");
|
||||
answers.token = pretoken;
|
||||
answers._connecting = true;
|
||||
// TODO use php-style object querification
|
||||
utils.putConfig('config', Object.keys(answers).map(function (key) {
|
||||
return key + ':' + answers[key];
|
||||
}), function (err/*, body*/) {
|
||||
if (err) {
|
||||
answers._error = err;
|
||||
console.error("Error while initializing config [connect]:");
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
//console.log("Pairing Enabled Locally");
|
||||
next();
|
||||
});
|
||||
}
|
||||
, offer: function (token, next) {
|
||||
//console.log("Pairing Enabled by Relay");
|
||||
answers.token = token;
|
||||
if (answers._error) {
|
||||
return;
|
||||
}
|
||||
if (answers._connecting) {
|
||||
return;
|
||||
}
|
||||
answers._connecting = true;
|
||||
utils.putConfig('config', Object.keys(answers).map(function (key) {
|
||||
return key + ':' + answers[key];
|
||||
}), function (err/*, body*/) {
|
||||
if (err) {
|
||||
answers._error = err;
|
||||
console.error("Error while initializing config [offer]:");
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
//console.log("Pairing Enabled Locally");
|
||||
next();
|
||||
});
|
||||
}
|
||||
, granted: function (_, next) {
|
||||
//console.log("Token has been granted!");
|
||||
next();
|
||||
}
|
||||
, end: function () {
|
||||
utils.putConfig('enable', [], function () {
|
||||
utils.putConfig('list', [], function (err) {
|
||||
if (err) { console.error(err); return; }
|
||||
console.info("Success");
|
||||
// workaround for https://github.com/nodejs/node/issues/21319
|
||||
if (answers._useTty) {
|
||||
setTimeout(function () {
|
||||
console.info();
|
||||
console.info("Press any key to continue...");
|
||||
console.info();
|
||||
process.exit(0);
|
||||
}, 0.5 * 1000);
|
||||
}
|
||||
// end workaround
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
help();
|
||||
process.exit(13);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -648,59 +619,167 @@ function parseConfig(err, text) {
|
|||
}
|
||||
|
||||
help();
|
||||
process.exit(11);
|
||||
}
|
||||
|
||||
function handleConfig(err, config) {
|
||||
//console.log('CONFIG');
|
||||
//console.log(config);
|
||||
state.config = config;
|
||||
|
||||
if (err) { console.error(err); process.exit(101); return; }
|
||||
|
||||
//
|
||||
// check for init first, before anything else
|
||||
// because it has arguments that may help in
|
||||
// the next steps
|
||||
//
|
||||
if (-1 !== argv.indexOf('init')) {
|
||||
parsers.init(argv, getToken);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.config.relay || !state.config.token) {
|
||||
if (!state.config.relay) {
|
||||
state.config.relay = 'telebit.cloud';
|
||||
}
|
||||
|
||||
console.log("question the user?", Date.now());
|
||||
askForConfig(state, function (err, state) {
|
||||
// no errors actually get passed, so this is just future-proofing
|
||||
if (err) { throw err; }
|
||||
|
||||
if (!state.config.token && state._can_pair) {
|
||||
state.config._otp = common.otp();
|
||||
}
|
||||
|
||||
console.log("done questioning:", Date.now());
|
||||
state.relay = state.config.relay;
|
||||
if (!state.token && !state.config.token) {
|
||||
getToken(err, state);
|
||||
} else {
|
||||
parseCli(state);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("no questioning:");
|
||||
parseCli(state);
|
||||
}
|
||||
|
||||
function parseConfig(err, text) {
|
||||
|
||||
console.info("");
|
||||
console.info(verstr.join(' '));
|
||||
|
||||
try {
|
||||
state._clientConfig = JSON.parse(text || '{}');
|
||||
} catch(e1) {
|
||||
try {
|
||||
state._clientConfig = YAML.safeLoad(text || '{}');
|
||||
} catch(e2) {
|
||||
console.error(e1.message);
|
||||
console.error(e2.message);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state._clientConfig = camelCopy(state._clientConfig || {}) || {};
|
||||
common._init(
|
||||
// make a default working dir and log dir
|
||||
state._clientConfig.root || path.join(os.homedir(), '.local/share/telebit')
|
||||
, (state._clientConfig.root && path.join(state._clientConfig.root, 'etc'))
|
||||
|| path.resolve(common.DEFAULT_CONFIG_PATH, '..')
|
||||
);
|
||||
state._ipc = common.pipename(state._clientConfig, true);
|
||||
|
||||
if (!Object.keys(state._clientConfig).length) {
|
||||
console.info('(' + state._ipc.comment + ": " + state._ipc.path + ')');
|
||||
console.info("");
|
||||
}
|
||||
|
||||
if ((err && 'ENOENT' === err.code) || !Object.keys(state._clientConfig).length) {
|
||||
if (!err || 'ENOENT' === err.code) {
|
||||
//console.warn("Empty config file. Run 'telebit init' to configure.\n");
|
||||
} else {
|
||||
console.warn("Couldn't load config:\n\n\t" + err.message + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
utils.request({ service: 'config' }, handleConfig);
|
||||
}
|
||||
|
||||
var parsers = {
|
||||
init: function (argv, cb) {
|
||||
init: function (argv, parseCb) {
|
||||
var answers = {};
|
||||
var bool = [
|
||||
'--advanced'
|
||||
];
|
||||
var boolish = [ '--advanced' ];
|
||||
if ('init' !== argv[0]) {
|
||||
throw new Error("init must be the first argument");
|
||||
}
|
||||
argv.shift();
|
||||
|
||||
utils.request({ service: 'config' }, function (err/*, body*/) {
|
||||
if (err) { cb(err); return; }
|
||||
// init --foo bar
|
||||
argv.forEach(function (arg, i) {
|
||||
if (!/^--/.test(arg)) { return; }
|
||||
if (-1 !== boolish.indexOf(arg)) {
|
||||
answers['_' + arg.replace(/^--/, '')] = true;
|
||||
}
|
||||
if (/^-/.test(argv[i + 1])) {
|
||||
throw new Error(argv[i + 1] + ' requires an argument');
|
||||
}
|
||||
answers[arg] = argv[i + 1];
|
||||
});
|
||||
|
||||
// init --foo bar
|
||||
argv.forEach(function (arg, i) {
|
||||
if (!/^--/.test(arg)) { return; }
|
||||
if (-1 !== bool.indexOf(arg)) {
|
||||
answers['_' + arg.replace(/^--/, '')] = true;
|
||||
}
|
||||
if (/^-/.test(argv[i + 1])) {
|
||||
throw new Error(argv[i + 1] + ' requires an argument');
|
||||
}
|
||||
answers[arg] = argv[i + 1];
|
||||
});
|
||||
// init foo:bar
|
||||
argv.forEach(function (arg) {
|
||||
if (/^--/.test(arg)) { return; }
|
||||
var parts = arg.split(/:/g);
|
||||
if (2 !== parts.length) {
|
||||
throw new Error("bad option to init: '" + arg + "'");
|
||||
}
|
||||
if (answers[parts[0]]) {
|
||||
throw new Error("duplicate key to init '" + parts[0] + "'");
|
||||
}
|
||||
answers[parts[0]] = parts[1];
|
||||
});
|
||||
// init foo:bar
|
||||
argv.forEach(function (arg) {
|
||||
if (/^--/.test(arg)) { return; }
|
||||
var parts = arg.split(/:/g);
|
||||
if (2 !== parts.length) {
|
||||
throw new Error("bad option to init: '" + arg + "'");
|
||||
}
|
||||
if (answers[parts[0]]) {
|
||||
throw new Error("duplicate key to init '" + parts[0] + "'");
|
||||
}
|
||||
answers[parts[0]] = parts[1];
|
||||
});
|
||||
|
||||
if (!answers._advanced && !answers.relay) {
|
||||
answers.relay = 'telebit.cloud';
|
||||
// things that aren't straight-forward copy-over
|
||||
if (!answers.advanced && !answers.relay) {
|
||||
answers.relay = 'telebit.cloud';
|
||||
}
|
||||
if (Array.isArray(common._NOTIFICATIONS[answers.update])) {
|
||||
common._NOTIFICATIONS[answers.update].forEach(function (name) {
|
||||
state.config[name] = true;
|
||||
});
|
||||
}
|
||||
if (answers.servernames) {
|
||||
state.config._servernames = answers.servernames;
|
||||
}
|
||||
if (answers.ports) {
|
||||
state.config._ports = answers.ports;
|
||||
}
|
||||
|
||||
// things that are straight-forward copy-over
|
||||
common.CONFIG_KEYS.forEach(function (key) {
|
||||
if ('true' === answers[key]) { answers[key] = true; }
|
||||
if ('false' === answers[key]) { answers[key] = false; }
|
||||
if ('null' === answers[key]) { answers[key] = null; }
|
||||
if ('undefined' === answers[key]) { delete answers[key]; }
|
||||
if ('undefined' !== typeof answers[key]) {
|
||||
state.config[key] = answers[key];
|
||||
}
|
||||
});
|
||||
|
||||
askForConfig(state, function (err, state) {
|
||||
if (err) { parseCb(err); return; }
|
||||
|
||||
if (!state.config.token && state._can_pair) {
|
||||
state.config._otp = common.otp();
|
||||
}
|
||||
|
||||
askForConfig(answers, function (err, answers) {
|
||||
if (err) { cb(err); return; }
|
||||
|
||||
if (!answers.token && answers._can_pair) {
|
||||
answers._otp = common.otp();
|
||||
}
|
||||
|
||||
cb(null, answers);
|
||||
});
|
||||
parseCb(null, state);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -168,8 +168,9 @@ function serveControlsHelper() {
|
|||
|
||||
// TODO camelCase query
|
||||
state.config.email = conf.email || state.config.email || '';
|
||||
if ('undefined' !== typeof conf.agree_tos) {
|
||||
state.config.agreeTos = conf.agree_tos;
|
||||
if ('undefined' !== typeof conf.agreeTos
|
||||
|| 'undefined' !== typeof conf.agreeTos ) {
|
||||
state.config.agreeTos = conf.agreeTos || conf.agree_tos;
|
||||
}
|
||||
state.otp = conf._otp || '0000'; // this should only be done on the client side
|
||||
state.config.relay = conf.relay || state.config.relay || '';
|
||||
|
@ -179,24 +180,28 @@ function serveControlsHelper() {
|
|||
if (state.secret) {
|
||||
state.token = common.signToken(state);
|
||||
}
|
||||
if (!state.token) {
|
||||
state.token = conf._token;
|
||||
}
|
||||
if ('undefined' !== typeof conf.newsletter) {
|
||||
state.config.newsletter = conf.newsletter;
|
||||
}
|
||||
if ('undefined' !== typeof conf.community_member) {
|
||||
state.config.communityMember = conf.community_member;
|
||||
if ('undefined' !== typeof conf.communityMember
|
||||
|| 'undefined' !== typeof conf.community_member) {
|
||||
state.config.communityMember = conf.communityMember || conf.community_member;
|
||||
}
|
||||
if ('undefined' !== typeof conf.telemetry) {
|
||||
state.config.telemetry = conf.telemetry;
|
||||
}
|
||||
if (conf.servernames) {
|
||||
(conf.servernames||'').split(/,/g).forEach(function (key) {
|
||||
if (conf._servernames) {
|
||||
(conf._servernames||'').split(/,/g).forEach(function (key) {
|
||||
if (!state.config.servernames[key]) {
|
||||
state.config.servernames[key] = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
if (conf.ports) {
|
||||
(conf.ports||'').split(/,/g).forEach(function (key) {
|
||||
if (conf._ports) {
|
||||
(conf._ports||'').split(/,/g).forEach(function (key) {
|
||||
if (!state.config.ports[key]) {
|
||||
state.config.ports[key] = {};
|
||||
}
|
||||
|
@ -475,7 +480,7 @@ function parseConfig(err, text) {
|
|||
state.config = {};
|
||||
}
|
||||
common._init(
|
||||
state.config.root || path.join(__dirname, '..')
|
||||
state.config.root || path.join(os.homedir(), '.local/share/telebit') // || path.join(__dirname, '..')
|
||||
, (state.config.root && path.join(state.config.root, 'etc')) || path.resolve(common.DEFAULT_CONFIG_PATH, '..')
|
||||
);
|
||||
state._ipc = common.pipename(state.config, true);
|
||||
|
|
|
@ -9,6 +9,27 @@ var os = require('os');
|
|||
var homedir = os.homedir();
|
||||
var urequest = require('@coolaj86/urequest');
|
||||
|
||||
common._NOTIFICATIONS = {
|
||||
'newsletter': [ 'newsletter', 'communityMember' ]
|
||||
, 'important': [ 'communityMember' ]
|
||||
};
|
||||
common.CONFIG_KEYS = [
|
||||
'newsletter'
|
||||
, 'communityMember'
|
||||
, 'telemetry'
|
||||
, 'sshAuto'
|
||||
, 'email'
|
||||
, 'agreeTos'
|
||||
, 'relay'
|
||||
, 'token'
|
||||
, 'pretoken'
|
||||
, 'secret'
|
||||
];
|
||||
//, '_servernames' // list instead of object
|
||||
//, '_ports' // list instead of object
|
||||
//, '_otp' // otp should not be saved
|
||||
//, '_token' // temporary token
|
||||
|
||||
common.pipename = function (config, newApi) {
|
||||
var _ipc = {
|
||||
path: (config.sock || common.DEFAULT_SOCK_PATH)
|
||||
|
@ -62,7 +83,7 @@ common.signToken = function (state) {
|
|||
port = parseInt(port, 10);
|
||||
return port > 0 && port <= 65535;
|
||||
})
|
||||
, aud: state.relayUrl
|
||||
, aud: state._relayUrl
|
||||
, iss: Math.round(Date.now() / 1000)
|
||||
};
|
||||
|
||||
|
@ -70,8 +91,8 @@ common.signToken = function (state) {
|
|||
};
|
||||
common.api = {};
|
||||
common.api.directory = function (state, next) {
|
||||
state.relayUrl = common.parseUrl(state.relay);
|
||||
urequest({ url: state.relayUrl + common.apiDirectory, json: true }, function (err, resp, dir) {
|
||||
state._relayUrl = common.parseUrl(state.relay);
|
||||
urequest({ url: state._relayUrl + common.apiDirectory, json: true }, function (err, resp, dir) {
|
||||
if (!dir) { dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; }
|
||||
state._apiDirectory = dir;
|
||||
next(err, dir);
|
||||
|
@ -81,9 +102,8 @@ common.api._parseWss = function (state, dir) {
|
|||
if (!dir || !dir.api_host) {
|
||||
dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } };
|
||||
}
|
||||
state.relayHostname = common.parseHostname(state.relay);
|
||||
state.wss = dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state.relayHostname) + dir.tunnel.pathname;
|
||||
return state.wss;
|
||||
state._relayHostname = common.parseHostname(state.relay);
|
||||
return dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state._relayHostname) + dir.tunnel.pathname;
|
||||
};
|
||||
common.api.wss = function (state, cb) {
|
||||
common.api.directory(state, function (err, dir) {
|
||||
|
@ -99,12 +119,13 @@ common.api.token = function (state, handlers) {
|
|||
|
||||
handlers.tunnelUrl(state.wss, function () {
|
||||
//console.log('[debug] after tunnelUrl');
|
||||
if (/*!state.config.token &&*/ state.config.secret) {
|
||||
state.token = common.signToken(state);
|
||||
if (state.config.secret /* && !state.config.token */) {
|
||||
state.config._token = common.signToken(state);
|
||||
}
|
||||
state.token = state.token || state.config.token;
|
||||
state.token = state.token || state.config.token || state.config._token;
|
||||
if (state.token) {
|
||||
//console.log('[debug] token via token or secret');
|
||||
// { token, pretoken }
|
||||
handlers.connect(state.token, function () {
|
||||
handlers.end(null, function () {});
|
||||
});
|
||||
|
@ -120,13 +141,13 @@ common.api.token = function (state, handlers) {
|
|||
|
||||
// TODO sign token with own private key, including public key and thumbprint
|
||||
// (much like ACME JOSE account)
|
||||
var otp = state.otp || state._otp || '0000'; // common.otp();
|
||||
var authReq = state.authRequest || state._auth || {
|
||||
var otp = state.config._otp; // common.otp();
|
||||
var authReq = {
|
||||
subject: state.config.email
|
||||
, subject_scheme: 'mailto'
|
||||
// TODO create domains list earlier
|
||||
, scope: Object.keys(state.config.servernames || {})
|
||||
.concat(Object.keys(state.config.ports || {})).join(',')
|
||||
, scope: (state.config._servernames || Object.keys(state.config.servernames || {}))
|
||||
.concat(state.config._ports || Object.keys(state.config.ports || {})).join(',')
|
||||
, otp: otp
|
||||
, hostname: os.hostname()
|
||||
// Used for User-Agent
|
||||
|
@ -135,7 +156,7 @@ common.api.token = function (state, handlers) {
|
|||
, os_release: os.release()
|
||||
, os_arch: os.arch()
|
||||
};
|
||||
var pairRequestUrl = url.resolve('https://' + dir.api_host.replace(/:hostname/g, state.relayHostname), dir.pair_request.pathname);
|
||||
var pairRequestUrl = url.resolve('https://' + dir.api_host.replace(/:hostname/g, state._relayHostname), dir.pair_request.pathname);
|
||||
var req = {
|
||||
url: pairRequestUrl
|
||||
, method: dir.pair_request.method
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var PromiseA;
|
||||
try {
|
||||
PromiseA = require('bluebird');
|
||||
} catch(e) {
|
||||
PromiseA = global.Promise;
|
||||
}
|
||||
var WebSocket = require('ws');
|
||||
var PromiseA = require('bluebird');
|
||||
var sni = require('sni');
|
||||
var Packer = require('proxy-packer');
|
||||
var os = require('os');
|
||||
|
@ -453,11 +458,11 @@ function _connect(state) {
|
|||
|
||||
if (!authenticated) {
|
||||
if(state.handlers.onError) {
|
||||
let err = new Error('Failed to connect on first attempt... check authentication');
|
||||
var err = new Error('Failed to connect on first attempt... check authentication');
|
||||
state.handlers.onError(err);
|
||||
}
|
||||
if(state.handlers.onClose) {
|
||||
state.handlers.onClose()
|
||||
state.handlers.onClose();
|
||||
}
|
||||
console.info('[close] failed on first attempt... check authentication.');
|
||||
timeoutId = null;
|
||||
|
@ -470,7 +475,7 @@ function _connect(state) {
|
|||
timeoutId = setTimeout(connect, 5000);
|
||||
} else {
|
||||
if(state.handlers.onClose) {
|
||||
state.handlers.onClose()
|
||||
state.handlers.onClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
package.json
12
package.json
|
@ -3,6 +3,10 @@
|
|||
"version": "0.18.9",
|
||||
"description": "Break out of localhost. Connect to any device from anywhere over any tcp port or securely in a browser. A secure tunnel. A poor man's reverse VPN.",
|
||||
"main": "lib/remote.js",
|
||||
"files": [
|
||||
"bin",
|
||||
"lib"
|
||||
],
|
||||
"bin": {
|
||||
"telebit": "bin/telebit.js",
|
||||
"telebitd": "bin/telebitd.js"
|
||||
|
@ -48,10 +52,9 @@
|
|||
},
|
||||
"homepage": "https://git.coolaj86.com/coolaj86/telebit.js#readme",
|
||||
"dependencies": {
|
||||
"@coolaj86/urequest": "^1.1.1",
|
||||
"bluebird": "^3.5.1",
|
||||
"@coolaj86/urequest": "^1.1.4",
|
||||
"finalhandler": "^1.1.1",
|
||||
"greenlock": "^2.2.20",
|
||||
"greenlock": "^2.3.1",
|
||||
"js-yaml": "^3.11.0",
|
||||
"jsonwebtoken": "^7.1.9",
|
||||
"mkdirp": "^0.5.1",
|
||||
|
@ -63,6 +66,9 @@
|
|||
"socket-pair": "^1.0.3",
|
||||
"ws": "^2.2.3"
|
||||
},
|
||||
"trulyOptionalDependencies": {
|
||||
"bluebird": "^3.5.1"
|
||||
},
|
||||
"enginesStrict": true,
|
||||
"engines": {
|
||||
"node": "10.2.1 10.4"
|
||||
|
|
|
@ -206,6 +206,9 @@ pushd $TELEBIT_TMP >/dev/null
|
|||
echo -n "."
|
||||
fi
|
||||
$tmp_npm install >/dev/null 2>/dev/null &
|
||||
# ursa is now an entirely optional dependency for key generation
|
||||
# but very much needed on ARM devices
|
||||
$tmp_npm install ursa >/dev/null 2>/dev/null &
|
||||
tmp_npm_pid=$!
|
||||
while [ -n "$tmp_npm_pid" ]; do
|
||||
sleep 2
|
||||
|
|
Loading…
Reference in New Issue