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

97 lines
4.0 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'),
DomainError = require('../domains.js').DomainError,
2017-02-15 00:47:10 -08:00
util = require('util');
2016-04-22 15:48:26 -07:00
function isChangeSynced(domain, value, type, nameserver, callback) {
assert.strictEqual(typeof domain, 'string');
2017-02-15 00:47:10 -08:00
assert(util.isRegExp(value));
2016-04-22 15:48:26 -07:00
assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof nameserver, 'string');
assert.strictEqual(typeof callback, 'function');
// ns records cannot have cname
dns.resolve4(nameserver, function (error, nsIps) {
2016-04-25 15:58:42 -07:00
if (error || !nsIps || nsIps.length === 0) {
debug('nameserver %s does not resolve. assuming it stays bad.', 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 10:21:31 -08:00
dns.resolve(domain, type, { server: nsIp, timeout: 5000 }, function (error, answer) {
2017-05-26 15:15:01 -07:00
if (error && error.code === 'ETIMEDOUT') {
debug('nameserver %s (%s) timed out when trying to resolve %s', nameserver, nsIp, domain);
return iteratorCallback(null, true); // should be ok if dns server is down
}
2016-04-25 16:00:49 -07:00
if (error) {
debug('nameserver %s (%s) returned error trying to resolve %s: %s', nameserver, nsIp, domain, error);
2017-02-16 20:11:09 -08:00
return iteratorCallback(null, false);
2016-04-25 16:00:49 -07:00
}
2016-04-22 15:48:26 -07:00
2016-04-25 16:00:49 -07:00
if (!answer || answer.length === 0) {
debug('bad answer from nameserver %s (%s) resolving %s (%s)', nameserver, nsIp, domain, type);
2017-02-16 20:11:09 -08:00
return iteratorCallback(null, false);
2016-04-25 16:00:49 -07:00
}
2016-04-25 16:16:14 -07:00
debug('isChangeSynced: ns: %s (%s), name:%s Actual:%j Expecting:%s', nameserver, nsIp, domain, answer, value);
2016-11-29 13:49:25 +01:00
var match = answer.some(function (a) {
2017-05-26 15:15:01 -07:00
return ((type === 'A' && value.test(a)) ||
(type === 'CNAME' && value.test(a)) ||
(type === 'TXT' && value.test(a)));
2016-11-29 13:49:25 +01:00
});
2017-02-16 20:11:09 -08:00
if (match) return iteratorCallback(null, true); // done!
2017-02-16 20:11:09 -08:00
iteratorCallback(null, false);
});
}, 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
2017-06-11 22:12:23 -07:00
function waitForDns(domain, zoneName, value, type, options, callback) {
assert.strictEqual(typeof domain, 'string');
2017-06-11 22:12:23 -07:00
assert.strictEqual(typeof zoneName, 'string');
2017-02-15 00:47:10 -08:00
assert(typeof value === 'string' || util.isRegExp(value));
assert(type === 'A' || type === 'CNAME' || type === 'TXT');
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');
2017-02-15 00:47:10 -08:00
if (typeof value === 'string') {
// http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
value = new RegExp('^' + value.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '$');
}
2016-04-22 15:48:26 -07:00
debug('waitForIp: domain %s to be %s in zone %s.', domain, value, zoneName);
2016-04-22 16:01:19 -07:00
var attempt = 1;
2016-06-21 15:12:36 -05:00
async.retry(options, function (retryCallback) {
2018-01-09 16:09:47 -08:00
debug('waitForDNS: %s (zone: %s) attempt %s.', domain, zoneName, attempt++);
dns.resolveNs(zoneName, function (error, nameservers) {
if (error || !nameservers) return retryCallback(error || new DomainError(DomainError.EXTERNAL_ERROR, 'Unable to get nameservers'));
2017-02-16 20:11:09 -08:00
async.every(nameservers, isChangeSynced.bind(null, domain, value, type), function (error, synced) {
2016-04-22 15:48:26 -07:00
debug('waitForIp: %s %s ns: %j', domain, synced ? 'done' : 'not done', nameservers);
retryCallback(synced ? null : new DomainError(DomainError.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);
debug('waitForDNS: %s done.', domain);
callback(null);
2018-01-09 16:09:47 -08:00
});
}