2017-06-26 17:34:42 +00:00
|
|
|
'use strict';
|
|
|
|
|
2017-09-14 21:26:19 +00:00
|
|
|
module.exports.create = function (deps, conf) {
|
2017-06-26 17:34:42 +00:00
|
|
|
var pending = {};
|
|
|
|
|
2017-09-19 19:18:22 +00:00
|
|
|
async function checkPublicAddr(host) {
|
2017-09-26 00:06:48 +00:00
|
|
|
var result = await deps.request({
|
2017-06-27 00:12:00 +00:00
|
|
|
method: 'GET'
|
2017-09-27 20:53:18 +00:00
|
|
|
, url: deps.OAUTH3.url.normalize(host)+'/api/org.oauth3.tunnel/checkip'
|
2017-06-27 00:12:00 +00:00
|
|
|
, json: true
|
|
|
|
});
|
2017-09-19 19:18:22 +00:00
|
|
|
|
|
|
|
if (!result.body) {
|
|
|
|
throw new Error('No response body in request for public address');
|
|
|
|
}
|
|
|
|
if (result.body.error) {
|
|
|
|
// Note that the error on the body will probably have a message that overwrites the default
|
|
|
|
throw Object.assign(new Error('error in check IP response'), result.body.error);
|
|
|
|
}
|
|
|
|
return result.body.address;
|
2017-06-27 00:12:00 +00:00
|
|
|
}
|
|
|
|
|
2017-09-19 19:18:22 +00:00
|
|
|
async function checkSinglePort(host, address, port) {
|
2017-06-26 17:34:42 +00:00
|
|
|
var crypto = require('crypto');
|
|
|
|
var token = crypto.randomBytes(8).toString('hex');
|
|
|
|
var keyAuth = crypto.randomBytes(32).toString('hex');
|
|
|
|
pending[token] = keyAuth;
|
|
|
|
|
2017-09-19 19:18:22 +00:00
|
|
|
var reqObj = {
|
2017-06-26 17:34:42 +00:00
|
|
|
method: 'POST'
|
2017-09-27 20:53:18 +00:00
|
|
|
, url: deps.OAUTH3.url.normalize(host)+'/api/org.oauth3.tunnel/loopback'
|
2017-09-19 19:18:22 +00:00
|
|
|
, json: {
|
|
|
|
address: address
|
|
|
|
, port: port
|
|
|
|
, token: token
|
|
|
|
, keyAuthorization: keyAuth
|
|
|
|
, iat: Date.now()
|
2017-06-27 00:12:00 +00:00
|
|
|
}
|
2017-09-19 19:18:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
var result;
|
|
|
|
try {
|
2017-09-26 00:06:48 +00:00
|
|
|
result = await deps.request(reqObj);
|
2017-09-19 19:18:22 +00:00
|
|
|
} catch (err) {
|
2017-06-27 00:12:00 +00:00
|
|
|
delete pending[token];
|
|
|
|
throw err;
|
2017-09-19 19:18:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
delete pending[token];
|
|
|
|
if (!result.body) {
|
|
|
|
throw new Error('No response body in loopback request for port '+port);
|
|
|
|
}
|
|
|
|
// If the loopback requests don't go to us then there are all kinds of ways it could
|
|
|
|
// error, but none of them really provide much extra information so we don't do
|
|
|
|
// anything that will break the PromiseA.all out and mask the other results.
|
|
|
|
if (conf.debug && result.body.error) {
|
|
|
|
console.log('error on remote side of port '+port+' loopback', result.body.error);
|
|
|
|
}
|
|
|
|
return !!result.body.success;
|
2017-06-27 00:12:00 +00:00
|
|
|
}
|
|
|
|
|
2017-09-19 19:18:22 +00:00
|
|
|
async function loopback(provider) {
|
|
|
|
var directives = await deps.OAUTH3.discover(provider);
|
|
|
|
var address = await checkPublicAddr(directives.api);
|
2017-09-26 00:06:48 +00:00
|
|
|
if (conf.debug) {
|
|
|
|
console.log('checking to see if', address, 'gets back to us');
|
|
|
|
}
|
2017-09-19 19:18:22 +00:00
|
|
|
|
2017-09-26 00:06:48 +00:00
|
|
|
var ports = require('../servers').listeners.tcp.list();
|
|
|
|
var values = await deps.PromiseA.all(ports.map(function (port) {
|
2017-09-19 19:18:22 +00:00
|
|
|
return checkSinglePort(directives.api, address, port);
|
|
|
|
}));
|
|
|
|
|
|
|
|
if (conf.debug) {
|
|
|
|
console.log('remaining loopback tokens', pending);
|
|
|
|
}
|
2017-09-14 21:26:19 +00:00
|
|
|
|
2017-09-27 20:53:18 +00:00
|
|
|
return {
|
|
|
|
address: address
|
|
|
|
, ports: ports.reduce(function (obj, port, ind) {
|
|
|
|
obj[port] = values[ind];
|
|
|
|
return obj;
|
|
|
|
}, {})
|
|
|
|
};
|
2017-06-26 17:34:42 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 21:26:19 +00:00
|
|
|
loopback.checkPublicAddr = checkPublicAddr;
|
2017-06-26 17:34:42 +00:00
|
|
|
loopback.server = require('http').createServer(function (req, res) {
|
|
|
|
var parsed = require('url').parse(req.url);
|
|
|
|
var token = parsed.pathname.replace('/.well-known/cloud-challenge/', '');
|
|
|
|
if (pending[token]) {
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.end(pending[token]);
|
|
|
|
} else {
|
|
|
|
res.statusCode = 404;
|
|
|
|
res.end();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return loopback;
|
|
|
|
};
|