From 40bd1d9cc63c0efeac36de972017cbaa65901e3d Mon Sep 17 00:00:00 2001 From: tigerbot Date: Tue, 7 Nov 2017 16:42:00 -0700 Subject: [PATCH] moved some functions into a utils files for wider use within ddns --- lib/ddns/dns-ctrl.js | 64 ++------------------------- lib/ddns/index.js | 57 ++++++------------------ lib/ddns/utils.js | 102 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 103 deletions(-) create mode 100644 lib/ddns/utils.js diff --git a/lib/ddns/dns-ctrl.js b/lib/ddns/dns-ctrl.js index 59eee35..4c19a59 100644 --- a/lib/ddns/dns-ctrl.js +++ b/lib/ddns/dns-ctrl.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports.create = function (deps, conf) { +module.exports.create = function (deps, conf, utils) { function dnsType(addr) { if (/^\d+\.\d+\.\d+\.\d+$/.test(addr)) { return 'A'; @@ -10,62 +10,6 @@ module.exports.create = function (deps, conf) { } } - var tldCache = {}; - async function getTlds(provider) { - async function updateCache() { - var reqObj = { - url: deps.OAUTH3.url.normalize(provider)+'/api/com.daplie.domains/prices' - , method: 'GET' - , json: true - }; - - var resp = await deps.OAUTH3.request(reqObj); - var tldObj = {}; - resp.data.forEach(function (tldInfo) { - if (tldInfo.enabled) { - tldObj[tldInfo.tld] = true; - } - }); - - tldCache[provider] = { - time: Date.now() - , tlds: tldObj - }; - return tldObj; - } - - // If we've never cached the results we need to return the promise that will fetch the recult, - // otherwise we can return the cached value. If the cached value has "expired", we can still - // return the cached value we just want to update the cache in parellel (making sure we only - // update once). - if (!tldCache[provider]) { - return updateCache(); - } - if (!tldCache[provider].updating && Date.now() - tldCache[provider].time > 24*60*60*1000) { - tldCache[provider].updating = true; - updateCache(); - } - - return tldCache[provider].tlds; - } - - async function splitDomains(provider, domains) { - var tlds = await getTlds(provider); - return domains.map(function (domain) { - var split = domain.split('.'); - var tldSegCnt = tlds[split.slice(-2).join('.')] ? 2 : 1; - - // Currently assuming that the sld can't contain dots, and that the tld can have at - // most one dot. Not 100% sure this is a valid assumption, but exceptions should be - // rare even if the assumption isn't valid. - return { - tld: split.slice(-tldSegCnt).join('.') - , sld: split.slice(-tldSegCnt-1, -tldSegCnt).join('.') - , sub: split.slice(0, -tldSegCnt-1).join('.') - }; - }); - } - async function setDeviceAddress(session, addr, domains) { var directives = await deps.OAUTH3.discover(session.token.aud); @@ -111,7 +55,7 @@ module.exports.create = function (deps, conf) { return goodAddrDomains.indexOf(domain) < 0; }); - var oldDns = await splitDomains(directives.api, badAddrDomains); + var oldDns = await utils.splitDomains(directives.api, badAddrDomains); var common = { api: 'devices.detach' , session: session @@ -124,7 +68,7 @@ module.exports.create = function (deps, conf) { console.log('removed bad DNS records for ' + badAddrDomains.join(', ')); } - var newDns = await splitDomains(directives.api, requiredUpdates); + var newDns = await utils.splitDomains(directives.api, requiredUpdates); common = { api: 'devices.attach' , session: session @@ -169,7 +113,7 @@ module.exports.create = function (deps, conf) { async function removeDomains(session, domains) { var directives = await deps.OAUTH3.discover(session.token.aud); - var oldDns = await splitDomains(directives.api, domains); + var oldDns = await utils.splitDomains(directives.api, domains); var common = { api: 'devices.detach' , session: session diff --git a/lib/ddns/index.js b/lib/ddns/index.js index fb72030..adce60d 100644 --- a/lib/ddns/index.js +++ b/lib/ddns/index.js @@ -3,48 +3,19 @@ module.exports.create = function (deps, conf) { var dns = deps.PromiseA.promisifyAll(require('dns')); 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 tunnelClients = require('./tunnel-client-manager').create(deps, conf); var equal = require('deep-equal'); + var utils = require('./utils').create(deps, conf); + var loopback = require('./loopback').create(deps, conf, utils); + var dnsCtrl = require('./dns-ctrl').create(deps, conf, utils); + var tunnelClients = require('./tunnel-client-manager').create(deps, conf, utils); + var loopbackDomain; - function iterateAllModules(action, curConf) { - curConf = curConf || conf; - var promises = curConf.ddns.modules.map(function (mod) { - return action(mod, mod.domains); - }); - - curConf.domains.forEach(function (dom) { - if (!dom.modules || !Array.isArray(dom.modules.ddns) || !dom.modules.ddns.length) { - return null; - } - - // For the time being all of our things should only be tried once (regardless if it succeeded) - // TODO: revisit this behavior when we support multiple ways of setting records, and/or - // if we want to allow later modules to run if early modules fail. - promises.push(dom.modules.ddns.reduce(function (prom, mod) { - if (prom) { return prom; } - return action(mod, dom.names); - }, null)); - }); - - return deps.PromiseA.all(promises.filter(Boolean)); - } - - async function getSession(id) { - var session = await deps.storage.tokens.get(id); - if (!session) { - throw new Error('no user token with ID "'+id+'"'); - } - return session; - } - var tunnelActive = false; async function startTunnel(tunnelSession, mod, domainList) { try { - var dnsSession = await getSession(mod.tokenId); + var dnsSession = await utils.getSession(mod.tokenId); var tunnelDomain = await tunnelClients.start(tunnelSession || dnsSession, domainList); var addrList; @@ -74,7 +45,7 @@ module.exports.create = function (deps, conf) { tunnelSession = await deps.storage.tokens.get(conf.ddns.tunnel.tokenId); } - await iterateAllModules(function (mod, domainList) { + await utils.iterateAllModules(function (mod, domainList) { if (mod.type !== 'dns@oauth3.org') { return null; } return startTunnel(tunnelSession, mod, domainList); @@ -90,7 +61,7 @@ module.exports.create = function (deps, conf) { async function checkTunnelTokens() { var oldTokens = tunnelClients.current(); - var newTokens = await iterateAllModules(function checkTokens(mod, domainList) { + var newTokens = await utils.iterateAllModules(function checkTokens(mod, domainList) { if (mod.type !== 'dns@oauth3.org') { return null; } var domainStr = domainList.slice().sort().join(','); @@ -188,10 +159,10 @@ module.exports.create = function (deps, conf) { } publicAddress = addr; - await iterateAllModules(function setModuleDNS(mod, domainList) { + await utils.iterateAllModules(function setModuleDNS(mod, domainList) { if (mod.type !== 'dns@oauth3.org' || mod.disabled) { return null; } - return getSession(mod.tokenId).then(function (session) { + return utils.getSession(mod.tokenId).then(function (session) { return dnsCtrl.setDeviceAddress(session, addr, domainList); }).catch(function (err) { console.log('error setting DNS records for', domainList.join(', ')); @@ -206,13 +177,13 @@ module.exports.create = function (deps, conf) { // this returns a Promise, but since the functions we use are synchronous // and change our enclosed variables we don't need to wait for the return. - iterateAllModules(function (mod, domainList) { + utils.iterateAllModules(function (mod, domainList) { if (mod.type !== 'dns@oauth3.org') { return; } prevMods[mod.id] = { mod, domainList }; return true; }, prevConf); - iterateAllModules(function (mod, domainList) { + utils.iterateAllModules(function (mod, domainList) { if (mod.type !== 'dns@oauth3.org') { return; } curMods[mod.id] = { mod, domainList }; @@ -250,7 +221,7 @@ module.exports.create = function (deps, conf) { return; } - return getSession(mod.tokenId).then(function (session) { + return utils.getSession(mod.tokenId).then(function (session) { return dnsCtrl.removeDomains(session, oldDomains); }); }).filter(Boolean)); @@ -275,7 +246,7 @@ module.exports.create = function (deps, conf) { return; } - return getSession(mod.tokenId).then(function (session) { + return utils.getSession(mod.tokenId).then(function (session) { return dnsCtrl.setDeviceAddress(session, publicAddress, newDomains); }); }).filter(Boolean)); diff --git a/lib/ddns/utils.js b/lib/ddns/utils.js new file mode 100644 index 0000000..6fe507c --- /dev/null +++ b/lib/ddns/utils.js @@ -0,0 +1,102 @@ +'use strict'; + +module.exports.create = function (deps, conf) { + + async function getSession(id) { + var session = await deps.storage.tokens.get(id); + if (!session) { + throw new Error('no user token with ID "' + id + '"'); + } + return session; + } + + function iterateAllModules(action, curConf) { + curConf = curConf || conf; + var promises = []; + + curConf.domains.forEach(function (dom) { + if (!dom.modules || !Array.isArray(dom.modules.ddns) || !dom.modules.ddns.length) { + return null; + } + + // For the time being all of our things should only be tried once (regardless if it succeeded) + // TODO: revisit this behavior when we support multiple ways of setting records, and/or + // if we want to allow later modules to run if early modules fail. + promises.push(dom.modules.ddns.reduce(function (prom, mod) { + if (prom) { return prom; } + return action(mod, dom.names); + }, null)); + }); + + curConf.ddns.modules.forEach(function (mod) { + promises.push(action(mod, mod.domains)); + }); + + return Promise.all(promises.filter(Boolean)); + } + + var tldCache = {}; + async function updateTldCache(provider) { + var reqObj = { + url: deps.OAUTH3.url.normalize(provider) + '/api/com.daplie.domains/prices' + , method: 'GET' + , json: true + }; + + var resp = await deps.OAUTH3.request(reqObj); + var tldObj = {}; + resp.data.forEach(function (tldInfo) { + if (tldInfo.enabled) { + tldObj[tldInfo.tld] = true; + } + }); + + tldCache[provider] = { + time: Date.now() + , tlds: tldObj + }; + return tldObj; + } + async function getTlds(provider) { + // If we've never cached the results we need to return the promise that will fetch the result, + // otherwise we can return the cached value. If the cached value has "expired", we can still + // return the cached value we just want to update the cache in parellel (making sure we only + // update once). + if (!tldCache[provider]) { + tldCache[provider] = { + updating: true + , tlds: updateTldCache(provider) + }; + } + if (!tldCache[provider].updating && Date.now() - tldCache[provider].time > 24 * 60 * 60 * 1000) { + tldCache[provider].updating = true; + updateTldCache(provider); + } + + return tldCache[provider].tlds; + } + + async function splitDomains(provider, domains) { + var tlds = await getTlds(provider); + return domains.map(function (domain) { + var split = domain.split('.'); + var tldSegCnt = tlds[split.slice(-2).join('.')] ? 2 : 1; + + // Currently assuming that the sld can't contain dots, and that the tld can have at + // most one dot. Not 100% sure this is a valid assumption, but exceptions should be + // rare even if the assumption isn't valid. + return { + tld: split.slice(-tldSegCnt).join('.') + , sld: split.slice(-tldSegCnt - 1, -tldSegCnt).join('.') + , sub: split.slice(0, -tldSegCnt - 1).join('.') + }; + }); + } + + return { + getSession + , iterateAllModules + , getTlds + , splitDomains + }; +};