2015-12-11 13:14:27 -08:00
|
|
|
'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'),
|
2017-10-29 01:48:55 +02:00
|
|
|
DomainError = require('../domains.js').DomainError,
|
2017-02-15 00:47:10 -08:00
|
|
|
util = require('util');
|
2015-12-11 13:14:27 -08:00
|
|
|
|
2016-04-22 15:48:26 -07:00
|
|
|
function isChangeSynced(domain, value, type, nameserver, callback) {
|
2015-12-11 13:14:27 -08:00
|
|
|
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');
|
2015-12-11 13:14:27 -08:00
|
|
|
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
|
|
|
}
|
2015-12-11 13:14:27 -08: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
|
|
|
|
|
}
|
2015-12-11 13:14:27 -08:00
|
|
|
|
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) {
|
2017-06-21 15:04:39 +02:00
|
|
|
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
|
|
|
}
|
2015-12-11 13:14:27 -08: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);
|
2015-12-11 13:14:27 -08:00
|
|
|
|
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!
|
2015-12-11 13:14:27 -08:00
|
|
|
|
2017-02-16 20:11:09 -08:00
|
|
|
iteratorCallback(null, false);
|
2015-12-11 13:14:27 -08:00
|
|
|
});
|
|
|
|
|
}, callback);
|
2017-05-26 15:15:01 -07:00
|
|
|
|
2015-12-11 13:14:27 -08:00
|
|
|
});
|
2018-01-09 16:09:47 -08:00
|
|
|
}
|
2015-12-11 13:14:27 -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) {
|
2015-12-11 13:14:27 -08:00
|
|
|
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');
|
2015-12-11 13:14:27 -08:00
|
|
|
|
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, '\\$&') + '$');
|
|
|
|
|
}
|
2017-06-21 15:04:39 +02:00
|
|
|
|
2016-04-22 15:48:26 -07:00
|
|
|
debug('waitForIp: domain %s to be %s in zone %s.', domain, value, zoneName);
|
2015-12-11 13:14:27 -08:00
|
|
|
|
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++);
|
2015-12-11 13:14:27 -08:00
|
|
|
|
|
|
|
|
dns.resolveNs(zoneName, function (error, nameservers) {
|
2017-10-29 01:48:55 +02:00
|
|
|
if (error || !nameservers) return retryCallback(error || new DomainError(DomainError.EXTERNAL_ERROR, 'Unable to get nameservers'));
|
2015-12-11 13:14:27 -08:00
|
|
|
|
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);
|
2015-12-11 13:14:27 -08:00
|
|
|
|
2017-10-29 01:48:55 +02:00
|
|
|
retryCallback(synced ? null : new DomainError(DomainError.EXTERNAL_ERROR, 'ETRYAGAIN'));
|
2015-12-11 13:14:27 -08:00
|
|
|
});
|
|
|
|
|
});
|
2016-04-22 16:01:19 -07:00
|
|
|
}, function retryDone(error) {
|
2018-01-09 16:09:47 -08:00
|
|
|
if (error) return callback(error);
|
2015-12-11 13:14:27 -08:00
|
|
|
|
|
|
|
|
debug('waitForDNS: %s done.', domain);
|
|
|
|
|
|
|
|
|
|
callback(null);
|
2018-01-09 16:09:47 -08:00
|
|
|
});
|
2015-12-11 13:14:27 -08:00
|
|
|
}
|