From 5eccf7dc3b5350c24802ec92cf9a257f9ce3a0c9 Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Tue, 11 Sep 2018 19:09:30 -0700 Subject: [PATCH] Enhance waitForDns to support TXT records --- src/apptask.js | 4 ++-- src/dns/noop.js | 3 ++- src/dns/waitfordns.js | 28 ++++++++++++++++++++-------- src/domains.js | 6 +++--- src/native-dns.js | 1 + src/setup.js | 2 +- 6 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/apptask.js b/src/apptask.js index 8d1ba0dd1..ffba421ff 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -417,13 +417,13 @@ function waitForDnsPropagation(app, callback) { sysinfo.getPublicIp(function (error, ip) { if (error) return callback(error); - domains.waitForDnsRecord(app.fqdn, app.domain, ip, { interval: 5000, times: 240 }, function (error) { + domains.waitForDnsRecord(app.fqdn, app.domain, 'A', ip, { interval: 5000, times: 240 }, function (error) { if (error) return callback(error); // now wait for alternateDomains, if any async.eachSeries(app.alternateDomains, function (domain, callback) { var fqdn = (domain.subdomain ? (domain.subdomain + '.') : '') + domain.domain; - domains.waitForDnsRecord(fqdn, domain.domain, ip, { interval: 5000, times: 240 }, callback); + domains.waitForDnsRecord(fqdn, domain.domain, 'A', ip, { interval: 5000, times: 240 }, callback); }, callback); }); }); diff --git a/src/dns/noop.js b/src/dns/noop.js index 366a7ee6c..c6f732637 100644 --- a/src/dns/noop.js +++ b/src/dns/noop.js @@ -46,9 +46,10 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) { return callback(); } -function waitForDns(domain, zoneName, value, options, callback) { +function waitForDns(domain, zoneName, type, value, options, callback) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof zoneName, 'string'); + assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof value, 'string'); assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 } assert.strictEqual(typeof callback, 'function'); diff --git a/src/dns/waitfordns.js b/src/dns/waitfordns.js index 57156b9ee..b5ee4f613 100644 --- a/src/dns/waitfordns.js +++ b/src/dns/waitfordns.js @@ -30,8 +30,9 @@ function resolveIp(hostname, options, callback) { }); } -function isChangeSynced(domain, value, nameserver, callback) { +function isChangeSynced(domain, type, value, nameserver, callback) { assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof value, 'string'); assert.strictEqual(typeof nameserver, 'string'); assert.strictEqual(typeof callback, 'function'); @@ -44,20 +45,30 @@ function isChangeSynced(domain, value, nameserver, callback) { } async.every(nsIps, function (nsIp, iteratorCallback) { - resolveIp(domain, { server: nsIp, timeout: 5000 }, function (error, answer) { + const resolveOptions = { server: nsIp, timeout: 5000 }; + const resolver = type === 'A' ? resolveIp.bind(null, domain) : dns.resolve.bind(null, domain, 'TXT'); + + resolver(resolveOptions, function (error, answer) { if (error && error.code === 'TIMEOUT') { - debug(`isChangeSynced: NS ${nameserver} (${nsIp}) timed out when resolving ${domain}`); + debug(`isChangeSynced: NS ${nameserver} (${nsIp}) timed out when resolving ${domain} (${type})`); return iteratorCallback(null, true); // should be ok if dns server is down } if (error) { - debug(`isChangeSynced: NS ${nameserver} (${nsIp}) errored when resolve ${domain}: ${error}`); + debug(`isChangeSynced: NS ${nameserver} (${nsIp}) errored when resolve ${domain} (${type}): ${error}`); return iteratorCallback(null, false); } - debug(`isChangeSynced: ${domain} was resolved to ${answer} at NS ${nameserver} (${nsIp}). Expecting ${value}`); + let match; + if (type === 'A') { + match = answer.length === 1 && answer[0] === value; + } else if (type === 'TXT') { // answer is a 2d array of strings + match = answer.some(function (a) { return value === a.join(''); }); + } - iteratorCallback(null, answer.length === 1 && answer[0] === value); + debug(`isChangeSynced: ${domain} (${type}) was resolved to ${answer} at NS ${nameserver} (${nsIp}). Expecting ${value}. Match ${match}`); + + iteratorCallback(null, match); }); }, callback); @@ -65,9 +76,10 @@ function isChangeSynced(domain, value, nameserver, callback) { } // check if IP change has propagated to every nameserver -function waitForDns(domain, zoneName, value, options, callback) { +function waitForDns(domain, zoneName, type, value, options, callback) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof zoneName, 'string'); + assert(type === 'A' || type === 'TXT'); assert.strictEqual(typeof value, 'string'); assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 } assert.strictEqual(typeof callback, 'function'); @@ -82,7 +94,7 @@ function waitForDns(domain, zoneName, value, options, callback) { dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { if (error || !nameservers) return retryCallback(error || new DomainsError(DomainsError.EXTERNAL_ERROR, 'Unable to get nameservers')); - async.every(nameservers, isChangeSynced.bind(null, domain, value), function (error, synced) { + async.every(nameservers, isChangeSynced.bind(null, domain, type, value), function (error, synced) { debug('waitForDns: %s %s ns: %j', domain, synced ? 'done' : 'not done', nameservers); retryCallback(synced ? null : new DomainsError(DomainsError.EXTERNAL_ERROR, 'ETRYAGAIN')); diff --git a/src/domains.js b/src/domains.js index a1dc80fa2..e3f801d38 100644 --- a/src/domains.js +++ b/src/domains.js @@ -397,10 +397,10 @@ function removeDnsRecords(subdomain, domain, type, values, callback) { }); } -// only wait for A record -function waitForDnsRecord(fqdn, domain, value, options, callback) { +function waitForDnsRecord(fqdn, domain, type, value, options, callback) { assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof domain, 'string'); + assert(type === 'A' || type === 'TXT'); assert.strictEqual(typeof value, 'string'); assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 } assert.strictEqual(typeof callback, 'function'); @@ -408,7 +408,7 @@ function waitForDnsRecord(fqdn, domain, value, options, callback) { get(domain, function (error, result) { if (error) return callback(error); - api(result.provider).waitForDns(fqdn, result ? result.zoneName : domain, value, options, callback); + api(result.provider).waitForDns(fqdn, result ? result.zoneName : domain, type, value, options, callback); }); } diff --git a/src/native-dns.js b/src/native-dns.js index f37e6cf87..b5ee59d20 100644 --- a/src/native-dns.js +++ b/src/native-dns.js @@ -30,6 +30,7 @@ function resolve(hostname, rrtype, options, callback) { // result is an empty array if there was no error but there is no record. when you query a random // domain, it errors with ENOTFOUND. But if you query an existing domain (A record) but with different // type (CNAME) it is not an error and empty array + // for TXT records, result is 2d array of strings callback(error, result); }); } diff --git a/src/setup.js b/src/setup.js index 211e80f24..3d1b2ba05 100644 --- a/src/setup.js +++ b/src/setup.js @@ -144,7 +144,7 @@ function configureWebadmin(callback) { debug('addWebadminDnsRecord: updated records with error:', error); if (error) return configureReverseProxy(error); - domains.waitForDnsRecord(config.adminFqdn(), config.adminDomain(), ip, { interval: 30000, times: 50000 }, function (error) { + domains.waitForDnsRecord(config.adminFqdn(), config.adminDomain(), 'A', ip, { interval: 30000, times: 50000 }, function (error) { if (error) return configureReverseProxy(error); gWebadminStatus.dns = true;