goldilocks.js/lib/ddns/index.js

158 lines
4.9 KiB
JavaScript
Raw Normal View History

2016-10-17 23:40:55 +00:00
'use strict';
2017-09-14 21:26:19 +00:00
module.exports.create = function (deps, conf) {
var network = deps.PromiseA.promisifyAll(deps.recase.camelCopy(require('network')));
var loopback = require('./loopback').create(deps, conf);
var dnsCtrl = require('./dns-ctrl').create(deps, conf);
2016-10-17 23:40:55 +00:00
var loopbackDomain;
function updateConf() {
loopbackDomain = 'oauth3.org';
if (conf.ddns && conf.ddns.loopback) {
if (conf.ddns.loopback.type === 'tunnel@oauth3.org' && conf.ddns.loopback.domain) {
loopbackDomain = conf.ddns.loopback.domain;
} else {
console.warn('invalid loopback configuration: bad type or missing domain');
}
}
}
updateConf();
var tunnelActive = false;
async function connectTunnel() {
var sessionCache = {};
var sessionOverride;
if (conf.ddns.tunnel) {
sessionOverride = await deps.storage.tokens.get(conf.ddns.tunnel.tokenId);
}
async function getSession(id) {
if (sessionOverride) {
return sessionOverride;
}
if (!sessionCache.hasOwnProperty(id)) {
sessionCache[id] = await deps.storage.tokens.get(conf.ddns.tunnel.tokenId);
}
if (!sessionCache[id]) {
throw new Error('no user token with ID "'+id+'"');
}
return sessionCache[id];
}
conf.domains.forEach(function(dom) {
if (dom.modules && Array.isArray(dom.modules.ddns) && dom.modules.ddns.length) {
var mod = dom.modules.ddns[0];
getSession(mod.token_id).then(function (session) {
return deps.tunnelClients.start(session, dom.names);
}).catch(function (err) {
console.log('error starting tunnel for', dom.names.join(', '));
console.log(err);
});
}
});
conf.ddns.modules.forEach(function (mod) {
getSession(mod.token_id).then(function (session) {
return deps.tunnelClients.start(session, mod.domains);
}).catch(function (err) {
console.log('error starting tunnel for', mod.domains.join(', '));
console.log(err);
});
});
tunnelActive = true;
}
function disconnectTunnel() {
deps.tunnelClients.disconnect();
tunnelActive = false;
}
var localAddr, gateway;
async function checkNetworkEnv() {
// Since we can't detect the OS level events when a user plugs in an ethernet cable to recheck
// what network environment we are in we check our local network address and the gateway to
// determine if we need to run the loopback check and router configuration again.
var gw = await network.getGatewayIpAsync();
var addr = await network.getPrivateIpAsync();
if (localAddr === addr && gateway === gw) {
return;
}
localAddr = addr;
gateway = gw;
var loopResult = await loopback(loopbackDomain);
var notLooped = Object.keys(loopResult.ports).filter(function (port) {
return !loopResult.ports[port];
});
// if (notLooped.length) {
// // TODO: try to automatically configure router to forward ports to us.
// }
// If we are on a public accress or all ports we are listening on are forwarded to us then
// we don't need the tunnel and we can set the DNS records for all our domains to our public
// address. Otherwise we need to use the tunnel to accept traffic.
if (!notLooped.length) {
if (tunnelActive) {
disconnectTunnel();
}
} else {
if (!tunnelActive) {
connectTunnel();
}
}
}
async function getSession() {
var sessions = await deps.storage.owners.all();
var session = sessions.filter(function (sess) {
return sess.token.scp.indexOf('dns') >= 0;
})[0];
if (!session) {
throw new Error('no sessions with DNS grants');
}
// The OAUTH3 library stores some things on the root session object that we usually
// just leave inside the token, but we need to pull those out before we use it here
session.provider_uri = session.provider_uri || session.token.provider_uri || session.token.iss;
session.client_uri = session.client_uri || session.token.azp;
session.scope = session.scope || session.token.scp;
return session;
}
2017-09-14 21:26:19 +00:00
var publicAddress;
async function recheckPubAddr() {
2017-09-14 21:26:19 +00:00
if (!conf.ddns.enabled) {
return;
}
await checkNetworkEnv();
if (tunnelActive) {
return;
}
var session = await getSession();
var addr = await loopback.checkPublicAddr(loopbackDomain);
2016-10-17 23:40:55 +00:00
if (publicAddress === addr) {
return;
}
if (conf.debug) {
console.log('previous public address',publicAddress, 'does not match current public address', addr);
}
await dnsCtrl.setDeviceAddress(session, addr, conf.ddns.domains);
publicAddress = addr;
2017-09-14 21:26:19 +00:00
}
2016-10-17 23:40:55 +00:00
2017-09-15 00:28:49 +00:00
recheckPubAddr();
2017-09-14 21:26:19 +00:00
setInterval(recheckPubAddr, 5*60*1000);
2016-10-17 23:40:55 +00:00
2017-09-14 21:26:19 +00:00
return {
loopbackServer: loopback.server
, setDeviceAddress: dnsCtrl.setDeviceAddress
, getDeviceAddresses: dnsCtrl.getDeviceAddresses
2017-09-14 21:26:19 +00:00
, recheckPubAddr: recheckPubAddr
, updateConf: updateConf
2016-10-17 23:40:55 +00:00
};
2017-09-14 21:26:19 +00:00
};