'use strict'; exports = module.exports = { refreshDns, sync }; const apps = require('./apps.js'), assert = require('assert'), dashboard = require('./dashboard.js'), debug = require('debug')('box:dyndns'), dns = require('./dns.js'), eventlog = require('./eventlog.js'), network = require('./network.js'), paths = require('./paths.js'), safe = require('safetydance'), tasks = require('./tasks.js'); // FIXME: this races with apptask. can result in a conflict if apptask is doing some dns operation and this code changes entries async function refreshDns(auditSource) { assert.strictEqual(typeof auditSource, 'object'); const ipv4 = await network.getIPv4(); const ipv6 = await network.getIPv6(); const info = safe.JSON.parse(safe.fs.readFileSync(paths.DYNDNS_INFO_FILE, 'utf8')) || { ipv4: null, ipv6: null }; const ipv4Changed = info.ipv4 !== ipv4; const ipv6Changed = ipv6 && info.ipv6 !== ipv6; // both should be RFC 5952 format if (!ipv4Changed && !ipv6Changed) { debug(`refreshDns: no change in IP. ipv4: ${ipv4} ipv6: ${ipv6}`); return; } debug(`refreshDns: updating IP from ${info.ipv4} to ipv4: ${ipv4} (changed: ${ipv4Changed}) ipv6: ${ipv6} (changed: ${ipv6Changed})`); // update dns in a separate task so we can have trackable logs const taskId = await tasks.add(tasks.TASK_SYNC_DYNDNS, [ ipv4Changed ? ipv4 : null, ipv6Changed ? ipv6 : null, auditSource ]); tasks.startTask(taskId, {}, async function (error) { if (error) { await eventlog.add(eventlog.ACTION_DYNDNS_UPDATE, auditSource, { taskId, fromIpv4: info.ipv4, fromIpv6: info.ipv6, toIpv4: ipv4, toIpv6: ipv6, errorMessage: error.message }); return; } await eventlog.add(eventlog.ACTION_DYNDNS_UPDATE, auditSource, { taskId, fromIpv4: info.ipv4, fromIpv6: info.ipv6, toIpv4: ipv4, toIpv6: ipv6 }); info.ipv4 = ipv4; info.ipv6 = ipv6; safe.fs.writeFileSync(paths.DYNDNS_INFO_FILE, JSON.stringify(info), 'utf8'); }); return taskId; } async function sync(ipv4, ipv6, auditSource, progressCallback) { assert(ipv4 === null || typeof ipv4 === 'string'); assert(ipv6 === null || typeof ipv6 === 'string'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof progressCallback, 'function'); let percent = 10; const { domain:dashboardDomain, fqdn:dashboardFqdn, subdomain:dashboardSubdomain } = await dashboard.getLocation(); progressCallback({ percent, message: `Updating dashboard domain ${dashboardFqdn}`}); if (ipv4) await safe(dns.upsertDnsRecords(dashboardSubdomain, dashboardDomain, 'A', [ ipv4 ]), { debug }); if (ipv6) await safe(dns.upsertDnsRecords(dashboardSubdomain, dashboardDomain, 'AAAA', [ ipv6 ]), { debug }); const result = await apps.list(); for (const app of result) { percent += Math.round(90/result.length); progressCallback({ percent, message: `Updating app ${app.fqdn}`}); const locations = [{ domain: app.domain, subdomain: app.subdomain }] .concat(app.secondaryDomains) .concat(app.redirectDomains) .concat(app.aliasDomains); for (const location of locations) { if (ipv4) await safe(dns.upsertDnsRecords(location.subdomain, location.domain, 'A', [ ipv4 ]), { debug }); if (ipv6) await safe(dns.upsertDnsRecords(location.subdomain, location.domain, 'AAAA', [ ipv6 ], { debug })); } } progressCallback({ percent: 100, message: 'refreshDNS: updated apps' }); }