'use strict'; 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); 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.error('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(); } } } var publicAddress; async function recheckPubAddr() { await checkNetworkEnv(); if (tunnelActive) { return; } var addr = await loopback.checkPublicAddr(loopbackDomain); if (publicAddress === addr) { return; } if (conf.debug) { console.log('previous public address',publicAddress, 'does not match current public address', addr); } publicAddress = addr; var sessionCache = {}; async function getSession(id) { 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.some(function (mod) { if (mod.type !== 'dns@oauth3.org' || mod.disabled) { return false; } return getSession(mod.token_id).then(function (session) { return dnsCtrl.setDeviceAddress(session, addr, dom.names); }).catch(function (err) { console.log('error setting DNS records for', dom.names.join(', ')); console.log(err); }); }); } }); conf.ddns.modules.forEach(function (mod) { if (mod.type !== 'dns@oauth3.org' || mod.disabled) { return; } getSession(mod.token_id).then(function (session) { return dnsCtrl.setDeviceAddress(session, addr, mod.domains); }).catch(function (err) { console.log('error setting DNS records for', mod.domains.join(', ')); console.log(err); }); }); } recheckPubAddr(); setInterval(recheckPubAddr, 5*60*1000); return { loopbackServer: loopback.server , setDeviceAddress: dnsCtrl.setDeviceAddress , getDeviceAddresses: dnsCtrl.getDeviceAddresses , recheckPubAddr: recheckPubAddr , updateConf: updateConf }; };