117 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict';
 | 
						|
 | 
						|
module.exports.create = function (deps, conf) {
 | 
						|
  var pending = {};
 | 
						|
 | 
						|
  async function _checkPublicAddr(host) {
 | 
						|
    var result = await deps.request({
 | 
						|
      method: 'GET'
 | 
						|
    , url: deps.OAUTH3.url.normalize(host)+'/api/org.oauth3.tunnel/checkip'
 | 
						|
    , json: true
 | 
						|
    });
 | 
						|
 | 
						|
    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);
 | 
						|
    }
 | 
						|
    if (!result.body.address) {
 | 
						|
      throw new Error("public address resonse doesn't contain address: "+JSON.stringify(result.body));
 | 
						|
    }
 | 
						|
    return result.body.address;
 | 
						|
  }
 | 
						|
  async function checkPublicAddr(provider) {
 | 
						|
    var directives = await deps.OAUTH3.discover(provider);
 | 
						|
    return _checkPublicAddr(directives.api);
 | 
						|
  }
 | 
						|
 | 
						|
  async function checkSinglePort(host, address, port) {
 | 
						|
    var crypto = require('crypto');
 | 
						|
    var token   = crypto.randomBytes(8).toString('hex');
 | 
						|
    var keyAuth = crypto.randomBytes(32).toString('hex');
 | 
						|
    pending[token] = keyAuth;
 | 
						|
 | 
						|
    var reqObj = {
 | 
						|
      method: 'POST'
 | 
						|
    , url: deps.OAUTH3.url.normalize(host)+'/api/org.oauth3.tunnel/loopback'
 | 
						|
    , timeout: 20*1000
 | 
						|
    , json: {
 | 
						|
        address: address
 | 
						|
      , port: port
 | 
						|
      , token: token
 | 
						|
      , keyAuthorization: keyAuth
 | 
						|
      , iat: Date.now()
 | 
						|
      , timeout: 18*1000
 | 
						|
      }
 | 
						|
    };
 | 
						|
 | 
						|
    var result;
 | 
						|
    try {
 | 
						|
      result = await deps.request(reqObj);
 | 
						|
    } catch (err) {
 | 
						|
      delete pending[token];
 | 
						|
      if (conf.debug) {
 | 
						|
        console.log('error making loopback request for port ' + port + ' loopback', err.message);
 | 
						|
      }
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    delete pending[token];
 | 
						|
    if (!result.body) {
 | 
						|
      if (conf.debug) {
 | 
						|
        console.log('No response body in loopback request for port '+port);
 | 
						|
      }
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    // 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;
 | 
						|
  }
 | 
						|
 | 
						|
  async function loopback(provider) {
 | 
						|
    var directives = await deps.OAUTH3.discover(provider);
 | 
						|
    var address = await _checkPublicAddr(directives.api);
 | 
						|
    if (conf.debug) {
 | 
						|
      console.log('checking to see if', address, 'gets back to us');
 | 
						|
    }
 | 
						|
 | 
						|
    var ports = require('../servers').listeners.tcp.list();
 | 
						|
    var values = await deps.PromiseA.all(ports.map(function (port) {
 | 
						|
      return checkSinglePort(directives.api, address, port);
 | 
						|
    }));
 | 
						|
 | 
						|
    if (conf.debug && Object.keys(pending).length) {
 | 
						|
      console.log('remaining loopback tokens', pending);
 | 
						|
    }
 | 
						|
 | 
						|
    return {
 | 
						|
      address: address
 | 
						|
    , ports: ports.reduce(function (obj, port, ind) {
 | 
						|
        obj[port] = values[ind];
 | 
						|
        return obj;
 | 
						|
      }, {})
 | 
						|
    };
 | 
						|
  }
 | 
						|
 | 
						|
  loopback.checkPublicAddr = checkPublicAddr;
 | 
						|
  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;
 | 
						|
};
 |