Files
cloudron-box/src/dns/waitfordns.js
T

99 lines
4.1 KiB
JavaScript
Raw Normal View History

'use strict';
exports = module.exports = waitForDns;
var assert = require('assert'),
async = require('async'),
2016-12-14 12:27:11 -08:00
debug = require('debug')('box:dns/waitfordns'),
2018-02-08 10:21:31 -08:00
dns = require('../native-dns.js'),
2018-04-29 11:20:12 -07:00
DomainsError = require('../domains.js').DomainsError;
2018-02-08 14:55:06 -08:00
function resolveIp(hostname, options, callback) {
assert.strictEqual(typeof hostname, 'string');
assert.strictEqual(typeof options, 'object');
assert.strictEqual(typeof callback, 'function');
// try A record at authoritative server
debug(`resolveIp: Checking if ${hostname} has A record at ${options.server}`);
dns.resolve(hostname, 'A', options, function (error, results) {
if (!error && results.length !== 0) return callback(null, results);
// try CNAME record at authoritative server
debug(`resolveIp: Checking if ${hostname} has CNAME record at ${options.server}`);
dns.resolve(hostname, 'CNAME', options, function (error, results) {
if (error || results.length === 0) return callback(error, results);
// recurse lookup the CNAME record
debug(`resolveIp: Resolving ${hostname}'s CNAME record ${results[0]}`);
dns.resolve(results[0], 'A', { server: '127.0.0.1', timeout: options.timeout }, callback);
});
});
}
2018-02-08 14:19:14 -08:00
function isChangeSynced(domain, value, nameserver, callback) {
assert.strictEqual(typeof domain, 'string');
2018-02-08 14:19:14 -08:00
assert.strictEqual(typeof value, 'string');
assert.strictEqual(typeof nameserver, 'string');
assert.strictEqual(typeof callback, 'function');
// ns records cannot have cname
2018-02-08 14:55:06 -08:00
dns.resolve(nameserver, 'A', { timeout: 5000 }, function (error, nsIps) {
2016-04-25 15:58:42 -07:00
if (error || !nsIps || nsIps.length === 0) {
2018-02-08 14:55:06 -08:00
debug(`isChangeSynced: cannot resolve NS ${nameserver}`); // it's fine if one or more ns are dead
2018-02-08 14:10:32 -08:00
return callback(null, true);
2016-04-25 15:58:42 -07:00
}
async.every(nsIps, function (nsIp, iteratorCallback) {
2018-02-08 14:55:06 -08:00
resolveIp(domain, { server: nsIp, timeout: 5000 }, function (error, answer) {
if (error && error.code === 'TIMEOUT') {
2018-02-08 14:55:06 -08:00
debug(`isChangeSynced: NS ${nameserver} (${nsIp}) timed out when resolving ${domain}`);
2017-05-26 15:15:01 -07:00
return iteratorCallback(null, true); // should be ok if dns server is down
}
2016-04-25 16:00:49 -07:00
if (error) {
2018-02-08 14:55:06 -08:00
debug(`isChangeSynced: NS ${nameserver} (${nsIp}) errored when resolve ${domain}: ${error}`);
2017-02-16 20:11:09 -08:00
return iteratorCallback(null, false);
2016-04-25 16:00:49 -07:00
}
2018-02-08 14:55:06 -08:00
debug(`isChangeSynced: ${domain} was resolved to ${answer} at NS ${nameserver} (${nsIp}). Expecting ${value}`);
2018-02-08 14:55:06 -08:00
iteratorCallback(null, answer.length === 1 && answer[0] === value);
});
}, callback);
2017-05-26 15:15:01 -07:00
});
2018-01-09 16:09:47 -08:00
}
// check if IP change has propagated to every nameserver
2018-02-08 14:19:14 -08:00
function waitForDns(domain, zoneName, value, options, callback) {
assert.strictEqual(typeof domain, 'string');
2017-06-11 22:12:23 -07:00
assert.strictEqual(typeof zoneName, 'string');
2018-02-08 14:19:14 -08:00
assert.strictEqual(typeof value, 'string');
2016-06-21 15:12:36 -05:00
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
2016-04-22 16:04:29 -07:00
assert.strictEqual(typeof callback, 'function');
2018-02-08 14:55:06 -08:00
debug('waitForDns: domain %s to be %s in zone %s.', domain, value, zoneName);
2018-02-08 14:55:06 -08:00
var attempt = 0;
2016-06-21 15:12:36 -05:00
async.retry(options, function (retryCallback) {
2018-02-08 14:55:06 -08:00
++attempt;
debug(`waitForDns (try ${attempt}): ${domain} to be ${value} in zone ${zoneName}`);
2018-02-08 14:39:35 -08:00
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
2018-04-29 11:20:12 -07:00
if (error || !nameservers) return retryCallback(error || new DomainsError(DomainsError.EXTERNAL_ERROR, 'Unable to get nameservers'));
2018-02-08 14:19:14 -08:00
async.every(nameservers, isChangeSynced.bind(null, domain, value), function (error, synced) {
2018-02-08 14:55:06 -08:00
debug('waitForDns: %s %s ns: %j', domain, synced ? 'done' : 'not done', nameservers);
2018-04-29 11:20:12 -07:00
retryCallback(synced ? null : new DomainsError(DomainsError.EXTERNAL_ERROR, 'ETRYAGAIN'));
});
});
2016-04-22 16:01:19 -07:00
}, function retryDone(error) {
2018-01-09 16:09:47 -08:00
if (error) return callback(error);
2018-02-08 14:55:06 -08:00
debug(`waitForDns: ${domain} has propagated`);
callback(null);
2018-01-09 16:09:47 -08:00
});
}