make waitForDns async
cloudflare is partly broken
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
resolve,
|
||||
};
|
||||
|
||||
const assert = require('assert'),
|
||||
constants = require('./constants.js'),
|
||||
dns = require('dns'),
|
||||
safe = require('safetydance'),
|
||||
_ = require('underscore');
|
||||
|
||||
// a note on TXT records. It doesn't have quotes ("") at the DNS level. Those quotes
|
||||
// are added for DNS server software to enclose spaces. Such quotes may also be returned
|
||||
// by the DNS REST API of some providers
|
||||
async function resolve(hostname, rrtype, options) {
|
||||
assert.strictEqual(typeof hostname, 'string');
|
||||
assert.strictEqual(typeof rrtype, 'string');
|
||||
assert(options && typeof options === 'object');
|
||||
|
||||
const defaultOptions = { server: '127.0.0.1', timeout: 5000 }; // unbound runs on 127.0.0.1
|
||||
const resolver = new dns.Resolver();
|
||||
options = _.extend({ }, defaultOptions, options);
|
||||
|
||||
// Only use unbound on a Cloudron
|
||||
if (constants.CLOUDRON) resolver.setServers([ options.server ]);
|
||||
|
||||
// should callback with ECANCELLED but looks like we might hit https://github.com/nodejs/node/issues/14814
|
||||
const timerId = setTimeout(resolver.cancel.bind(resolver), options.timeout || 5000);
|
||||
|
||||
const [error, result] = safe(resolver.resolve(hostname, rrtype));
|
||||
clearTimeout(timerId);
|
||||
|
||||
if (error && error.code === 'ECANCELLED') error.code = 'TIMEOUT';
|
||||
if (error) throw error;
|
||||
|
||||
// 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
|
||||
return result;
|
||||
}
|
||||
+34
-74
@@ -19,12 +19,6 @@ module.exports = exports = {
|
||||
|
||||
checkDnsRecords,
|
||||
syncDnsRecords,
|
||||
|
||||
resolve,
|
||||
|
||||
promises: {
|
||||
resolve: require('util').promisify(resolve)
|
||||
}
|
||||
};
|
||||
|
||||
const apps = require('./apps.js'),
|
||||
@@ -32,7 +26,6 @@ const apps = require('./apps.js'),
|
||||
BoxError = require('./boxerror.js'),
|
||||
constants = require('./constants.js'),
|
||||
debug = require('debug')('box:dns'),
|
||||
dns = require('dns'),
|
||||
domains = require('./domains.js'),
|
||||
ipaddr = require('ipaddr.js'),
|
||||
mail = require('./mail.js'),
|
||||
@@ -41,8 +34,7 @@ const apps = require('./apps.js'),
|
||||
settings = require('./settings.js'),
|
||||
sysinfo = require('./sysinfo.js'),
|
||||
tld = require('tldjs'),
|
||||
util = require('util'),
|
||||
_ = require('underscore');
|
||||
util = require('util');
|
||||
|
||||
// choose which subdomain backend we use for test purpose we use route53
|
||||
function api(provider) {
|
||||
@@ -67,30 +59,30 @@ function api(provider) {
|
||||
}
|
||||
}
|
||||
|
||||
function fqdn(location, domainObject) {
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
function fqdn(subdomain, domainObject) {
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
|
||||
return location + (location ? '.' : '') + domainObject.domain;
|
||||
return subdomain + (subdomain ? '.' : '') + domainObject.domain;
|
||||
}
|
||||
|
||||
// Hostname validation comes from RFC 1123 (section 2.1)
|
||||
// Domain name validation comes from RFC 2181 (Name syntax)
|
||||
// https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
|
||||
// We are validating the validity of the location-fqdn as host name (and not dns name)
|
||||
function validateHostname(location, domainObject) {
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
function validateHostname(subdomain, domainObject) {
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
|
||||
const hostname = fqdn(location, domainObject);
|
||||
const hostname = fqdn(subdomain, domainObject);
|
||||
|
||||
const RESERVED_LOCATIONS = [
|
||||
constants.SMTP_LOCATION,
|
||||
constants.IMAP_LOCATION
|
||||
];
|
||||
if (RESERVED_LOCATIONS.indexOf(location) !== -1) return new BoxError(BoxError.BAD_FIELD, location + ' is reserved', { field: 'location' });
|
||||
if (RESERVED_LOCATIONS.indexOf(subdomain) !== -1) return new BoxError(BoxError.BAD_FIELD, subdomain + ' is reserved', { field: 'location' });
|
||||
|
||||
if (hostname === settings.dashboardFqdn()) return new BoxError(BoxError.BAD_FIELD, location + ' is reserved', { field: 'location' });
|
||||
if (hostname === settings.dashboardFqdn()) return new BoxError(BoxError.BAD_FIELD, subdomain + ' is reserved', { field: 'location' });
|
||||
|
||||
// workaround https://github.com/oncletom/tld.js/issues/73
|
||||
var tmp = hostname.replace('_', '-');
|
||||
@@ -98,11 +90,11 @@ function validateHostname(location, domainObject) {
|
||||
|
||||
if (hostname.length > 253) return new BoxError(BoxError.BAD_FIELD, 'Hostname length exceeds 253 characters', { field: 'location' });
|
||||
|
||||
if (location) {
|
||||
if (subdomain) {
|
||||
// label validation
|
||||
if (location.split('.').some(function (p) { return p.length > 63 || p.length < 1; })) return new BoxError(BoxError.BAD_FIELD, 'Invalid subdomain length', { field: 'location' });
|
||||
if (location.match(/^[A-Za-z0-9-.]+$/) === null) return new BoxError(BoxError.BAD_FIELD, 'Subdomain can only contain alphanumeric, hyphen and dot', { field: 'location' });
|
||||
if (/^[-.]/.test(location)) return new BoxError(BoxError.BAD_FIELD, 'Subdomain cannot start or end with hyphen or dot', { field: 'location' });
|
||||
if (subdomain.split('.').some(function (p) { return p.length > 63 || p.length < 1; })) return new BoxError(BoxError.BAD_FIELD, 'Invalid subdomain length', { field: 'location' });
|
||||
if (subdomain.match(/^[A-Za-z0-9-.]+$/) === null) return new BoxError(BoxError.BAD_FIELD, 'Subdomain can only contain alphanumeric, hyphen and dot', { field: 'location' });
|
||||
if (/^[-.]/.test(subdomain)) return new BoxError(BoxError.BAD_FIELD, 'Subdomain cannot start or end with hyphen or dot', { field: 'location' });
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -110,12 +102,12 @@ function validateHostname(location, domainObject) {
|
||||
|
||||
// returns the 'name' that needs to be inserted into zone
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function getName(domain, location, type) {
|
||||
function getName(domain, subdomain, type) {
|
||||
const part = domain.domain.slice(0, -domain.zoneName.length - 1);
|
||||
|
||||
if (location === '') return part;
|
||||
if (subdomain === '') return part;
|
||||
|
||||
return part ? `${location}.${part}` : location;
|
||||
return part ? `${subdomain}.${part}` : subdomain;
|
||||
}
|
||||
|
||||
function maybePromisify(func) {
|
||||
@@ -123,20 +115,20 @@ function maybePromisify(func) {
|
||||
return util.promisify(func);
|
||||
}
|
||||
|
||||
async function getDnsRecords(location, domain, type) {
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
async function getDnsRecords(subdomain, domain, type) {
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
assert.strictEqual(typeof type, 'string');
|
||||
|
||||
const domainObject = await domains.get(domain);
|
||||
return await maybePromisify(api(domainObject.provider).get)(domainObject, location, type);
|
||||
return await maybePromisify(api(domainObject.provider).get)(domainObject, subdomain, type);
|
||||
}
|
||||
|
||||
async function checkDnsRecords(location, domain) {
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
async function checkDnsRecords(subdomain, domain) {
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
|
||||
const ipv4Records = await getDnsRecords(location, domain, 'A');
|
||||
const ipv4Records = await getDnsRecords(subdomain, domain, 'A');
|
||||
const ipv4 = await sysinfo.getServerIPv4();
|
||||
|
||||
// if empty OR exactly one record with the ip, we don't need to overwrite
|
||||
@@ -144,7 +136,7 @@ async function checkDnsRecords(location, domain) {
|
||||
|
||||
const ipv6Enabled = await settings.getIPv6Config();
|
||||
if (ipv6Enabled) {
|
||||
const ipv6Records = await getDnsRecords(location, domain, 'AAAA');
|
||||
const ipv6Records = await getDnsRecords(subdomain, domain, 'AAAA');
|
||||
const ipv6 = await sysinfo.getServerIPv6();
|
||||
|
||||
// if empty OR exactly one record with the ip, we don't need to overwrite
|
||||
@@ -155,33 +147,33 @@ async function checkDnsRecords(location, domain) {
|
||||
}
|
||||
|
||||
// note: for TXT records the values must be quoted
|
||||
async function upsertDnsRecords(location, domain, type, values) {
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
async function upsertDnsRecords(subdomain, domain, type, values) {
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
assert.strictEqual(typeof type, 'string');
|
||||
assert(Array.isArray(values));
|
||||
|
||||
debug(`upsertDNSRecord: location ${location} on domain ${domain} of type ${type} with values ${JSON.stringify(values)}`);
|
||||
debug(`upsertDNSRecord: location ${subdomain} on domain ${domain} of type ${type} with values ${JSON.stringify(values)}`);
|
||||
|
||||
const domainObject = await domains.get(domain);
|
||||
await maybePromisify(api(domainObject.provider).upsert)(domainObject, location, type, values);
|
||||
await maybePromisify(api(domainObject.provider).upsert)(domainObject, subdomain, type, values);
|
||||
}
|
||||
|
||||
async function removeDnsRecords(location, domain, type, values) {
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
async function removeDnsRecords(subdomain, domain, type, values) {
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
assert.strictEqual(typeof type, 'string');
|
||||
assert(Array.isArray(values));
|
||||
|
||||
debug('removeDNSRecord: %s on %s type %s values', location, domain, type, values);
|
||||
debug('removeDNSRecord: %s on %s type %s values', subdomain, domain, type, values);
|
||||
|
||||
const domainObject = await domains.get(domain);
|
||||
const [error] = await safe(maybePromisify(api(domainObject.provider).del)(domainObject, location, type, values));
|
||||
const [error] = await safe(maybePromisify(api(domainObject.provider).del)(domainObject, subdomain, type, values));
|
||||
if (error && error.reason !== BoxError.NOT_FOUND) throw error;
|
||||
}
|
||||
|
||||
async function waitForDnsRecord(location, domain, type, value, options) {
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
async function waitForDnsRecord(subdomain, domain, type, value, options) {
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
assert(type === 'A' || type === 'AAAA' || type === 'TXT');
|
||||
assert.strictEqual(typeof value, 'string');
|
||||
@@ -192,7 +184,7 @@ async function waitForDnsRecord(location, domain, type, value, options) {
|
||||
// linode DNS takes ~15mins
|
||||
if (!options.interval) options.interval = domainObject.provider === 'linode' ? 20000 : 5000;
|
||||
|
||||
await maybePromisify(api(domainObject.provider).wait)(domainObject, location, type, value, options);
|
||||
await api(domainObject.provider).wait(domainObject, subdomain, type, value, options);
|
||||
}
|
||||
|
||||
function makeWildcard(vhost) {
|
||||
@@ -318,35 +310,3 @@ async function syncDnsRecords(options, progressCallback) {
|
||||
|
||||
return { errors };
|
||||
}
|
||||
|
||||
// a note on TXT records. It doesn't have quotes ("") at the DNS level. Those quotes
|
||||
// are added for DNS server software to enclose spaces. Such quotes may also be returned
|
||||
// by the DNS REST API of some providers
|
||||
function resolve(hostname, rrtype, options, callback) {
|
||||
assert.strictEqual(typeof hostname, 'string');
|
||||
assert.strictEqual(typeof rrtype, 'string');
|
||||
assert(options && typeof options === 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
const defaultOptions = { server: '127.0.0.1', timeout: 5000 }; // unbound runs on 127.0.0.1
|
||||
const resolver = new dns.Resolver();
|
||||
options = _.extend({ }, defaultOptions, options);
|
||||
|
||||
// Only use unbound on a Cloudron
|
||||
if (constants.CLOUDRON) resolver.setServers([ options.server ]);
|
||||
|
||||
// should callback with ECANCELLED but looks like we might hit https://github.com/nodejs/node/issues/14814
|
||||
const timerId = setTimeout(resolver.cancel.bind(resolver), options.timeout || 5000);
|
||||
|
||||
resolver.resolve(hostname, rrtype, function (error, result) {
|
||||
clearTimeout(timerId);
|
||||
|
||||
if (error && error.code === 'ECANCELLED') error.code = 'TIMEOUT';
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
+10
-16
@@ -245,36 +245,30 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const domainConfig = domainObject.config,
|
||||
zoneName = domainObject.zoneName,
|
||||
fqdn = dns.fqdn(location, domainObject);
|
||||
fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
debug('wait: %s for zone %s of type %s', fqdn, zoneName, type);
|
||||
|
||||
getZoneByName(domainConfig, zoneName, function(error, result) {
|
||||
if (error) return callback(error);
|
||||
const result = await getZoneByName(domainConfig, zoneName);
|
||||
const zoneId = result.id;
|
||||
|
||||
let zoneId = result.id;
|
||||
const dnsRecords = await getDnsRecords(domainConfig, zoneId, fqdn, type);
|
||||
if (dnsRecords.length === 0) throw new BoxError(BoxError.NOT_FOUND, 'Domain not found');
|
||||
|
||||
getDnsRecords(domainConfig, zoneId, fqdn, type, function (error, dnsRecords) {
|
||||
if (error) return callback(error);
|
||||
if (dnsRecords.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'Domain not found'));
|
||||
if (!dnsRecords[0].proxied) return await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
|
||||
if (!dnsRecords[0].proxied) return waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
debug('wait: skipping wait of proxied domain');
|
||||
|
||||
debug('wait: skipping wait of proxied domain');
|
||||
|
||||
callback(null); // maybe we can check for dns to be cloudflare IPs? https://api.cloudflare.com/#cloudflare-ips-cloudflare-ip-details
|
||||
});
|
||||
});
|
||||
// maybe we can check for dns to be cloudflare IPs? https://api.cloudflare.com/#cloudflare-ips-cloudflare-ip-details
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
@@ -221,17 +221,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+4
-5
@@ -121,17 +121,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+4
-5
@@ -173,17 +173,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+4
-5
@@ -156,17 +156,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+10
-14
@@ -29,9 +29,9 @@ function injectPrivateFields(newConfig, currentConfig) {
|
||||
// in-place injection of tokens and api keys which came in with constants.SECRET_PLACEHOLDER
|
||||
}
|
||||
|
||||
function upsert(domainObject, location, type, values, callback) {
|
||||
function upsert(domainObject, subdomain, type, values, callback) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof type, 'string');
|
||||
assert(Array.isArray(values));
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
@@ -41,9 +41,9 @@ function upsert(domainObject, location, type, values, callback) {
|
||||
callback(new BoxError(BoxError.NOT_IMPLEMENTED, 'upsert is not implemented'));
|
||||
}
|
||||
|
||||
function get(domainObject, location, type, callback) {
|
||||
function get(domainObject, subdomain, type, callback) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof type, 'string');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
@@ -52,9 +52,9 @@ function get(domainObject, location, type, callback) {
|
||||
callback(new BoxError(BoxError.NOT_IMPLEMENTED, 'get is not implemented'));
|
||||
}
|
||||
|
||||
function del(domainObject, location, type, values, callback) {
|
||||
function del(domainObject, subdomain, type, values, callback) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof type, 'string');
|
||||
assert(Array.isArray(values));
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
@@ -64,22 +64,18 @@ function del(domainObject, location, type, values, callback) {
|
||||
callback(new BoxError(BoxError.NOT_IMPLEMENTED, 'del is not implemented'));
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
async function verifyDomainConfig(domainObject) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
// Result: domainConfig object
|
||||
|
||||
callback(new BoxError(BoxError.NOT_IMPLEMENTED, 'verifyDomainConfig is not implemented'));
|
||||
throw new BoxError(BoxError.NOT_IMPLEMENTED, 'verifyDomainConfig is not implemented');
|
||||
}
|
||||
|
||||
+4
-5
@@ -254,17 +254,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+4
-5
@@ -56,17 +56,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
@@ -305,15 +305,14 @@ function verifyDomainConfig(domainObject, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, subdomain, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
+4
-5
@@ -225,17 +225,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+4
-5
@@ -232,17 +232,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+2
-3
@@ -52,15 +52,14 @@ function del(domainObject, location, type, values, callback) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, location, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, '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');
|
||||
|
||||
callback();
|
||||
// do nothing
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+4
-5
@@ -231,17 +231,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+4
-5
@@ -221,17 +221,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
function verifyDomainConfig(domainObject, callback) {
|
||||
|
||||
+58
-68
@@ -3,35 +3,33 @@
|
||||
exports = module.exports = waitForDns;
|
||||
|
||||
const assert = require('assert'),
|
||||
async = require('async'),
|
||||
BoxError = require('../boxerror.js'),
|
||||
debug = require('debug')('box:dns/waitfordns'),
|
||||
dns = require('../dns.js');
|
||||
dig = require('../dig.js'),
|
||||
promiseRetry = require('../promise-retry.js'),
|
||||
safe = require('safetydance');
|
||||
|
||||
function resolveIp(hostname, type, options, callback) {
|
||||
async function resolveIp(hostname, type, options) {
|
||||
assert.strictEqual(typeof hostname, 'string');
|
||||
assert(type === 'A' || type === 'AAAA');
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
// try A record at authoritative server
|
||||
debug(`resolveIp: Checking if ${hostname} has ${type} record at ${options.server}`);
|
||||
dns.resolve(hostname, type, options, function (error, results) {
|
||||
if (!error && results.length !== 0) return callback(null, results);
|
||||
const [error, results] = await safe(dig.resolve(hostname, type, options));
|
||||
if (!error && results.length !== 0) return 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);
|
||||
// try CNAME record at authoritative server
|
||||
debug(`resolveIp: Checking if ${hostname} has CNAME record at ${options.server}`);
|
||||
const cnameResults = await dig.resolve(hostname, 'CNAME', options);
|
||||
if (cnameResults.length === 0) return cnameResults;
|
||||
|
||||
// recurse lookup the CNAME record
|
||||
debug(`resolveIp: Resolving ${hostname}'s CNAME record ${results[0]}`);
|
||||
dns.resolve(results[0], type, { server: '127.0.0.1', timeout: options.timeout }, callback);
|
||||
});
|
||||
});
|
||||
// recurse lookup the CNAME record
|
||||
debug(`resolveIp: Resolving ${hostname}'s CNAME record ${results[0]}`);
|
||||
await dig.resolve(results[0], type, options);
|
||||
}
|
||||
|
||||
function isChangeSynced(hostname, type, value, nameserver, callback) {
|
||||
async function isChangeSynced(hostname, type, value, nameserver) {
|
||||
assert.strictEqual(typeof hostname, 'string');
|
||||
assert.strictEqual(typeof type, 'string');
|
||||
assert.strictEqual(typeof value, 'string');
|
||||
@@ -39,73 +37,65 @@ function isChangeSynced(hostname, type, value, nameserver, callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
// ns records cannot have cname
|
||||
dns.resolve(nameserver, 'A', { timeout: 5000 }, function (error, nsIps) {
|
||||
if (error || !nsIps || nsIps.length === 0) {
|
||||
debug(`isChangeSynced: cannot resolve NS ${nameserver}`); // it's fine if one or more ns are dead
|
||||
return callback(null, true);
|
||||
const [error, nsIps] = await safe(dig.resolve(nameserver, 'A', { timeout: 5000 }));
|
||||
if (error || !nsIps || nsIps.length === 0) {
|
||||
debug(`isChangeSynced: cannot resolve NS ${nameserver}`); // it's fine if one or more ns are dead
|
||||
return true;
|
||||
}
|
||||
|
||||
const status = [];
|
||||
for (let i = 0; i < nsIps.length; i++) {
|
||||
const nsIp = nsIps[i];
|
||||
const resolveOptions = { server: nsIp, timeout: 5000 };
|
||||
const resolver = type === 'A' || type === 'AAAA' ? resolveIp(hostname, type, resolveOptions) : dig.resolve(hostname, 'TXT', resolveOptions);
|
||||
|
||||
const [error, answer] = await safe(resolver);
|
||||
if (error && error.code === 'TIMEOUT') {
|
||||
debug(`isChangeSynced: NS ${nameserver} (${nsIp}) timed out when resolving ${hostname} (${type})`);
|
||||
status[i] = true; // should be ok if dns server is down
|
||||
continue;
|
||||
}
|
||||
|
||||
async.every(nsIps, function (nsIp, iteratorCallback) {
|
||||
const resolveOptions = { server: nsIp, timeout: 5000 };
|
||||
const resolver = type === 'A' || type === 'AAAA' ? resolveIp.bind(null, hostname, type) : dns.resolve.bind(null, hostname, 'TXT');
|
||||
if (error) {
|
||||
debug(`isChangeSynced: NS ${nameserver} (${nsIp}) errored when resolve ${hostname} (${type}): ${error}`);
|
||||
status[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
resolver(resolveOptions, function (error, answer) {
|
||||
if (error && error.code === 'TIMEOUT') {
|
||||
debug(`isChangeSynced: NS ${nameserver} (${nsIp}) timed out when resolving ${hostname} (${type})`);
|
||||
return iteratorCallback(null, true); // should be ok if dns server is down
|
||||
}
|
||||
let match;
|
||||
if (type === 'A' || type === 'AAAA') {
|
||||
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(''); });
|
||||
}
|
||||
|
||||
if (error) {
|
||||
debug(`isChangeSynced: NS ${nameserver} (${nsIp}) errored when resolve ${hostname} (${type}): ${error}`);
|
||||
return iteratorCallback(null, false);
|
||||
}
|
||||
debug(`isChangeSynced: ${hostname} (${type}) was resolved to ${answer} at NS ${nameserver} (${nsIp}). Expecting ${value}. Match ${match}`);
|
||||
status[i] = match;
|
||||
}
|
||||
|
||||
let match;
|
||||
if (type === 'A' || type === 'AAAA') {
|
||||
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(''); });
|
||||
}
|
||||
|
||||
debug(`isChangeSynced: ${hostname} (${type}) was resolved to ${answer} at NS ${nameserver} (${nsIp}). Expecting ${value}. Match ${match}`);
|
||||
|
||||
iteratorCallback(null, match);
|
||||
});
|
||||
}, callback);
|
||||
|
||||
});
|
||||
return status.every(s => s === true);
|
||||
}
|
||||
|
||||
// check if IP change has propagated to every nameserver
|
||||
function waitForDns(hostname, zoneName, type, value, options, callback) {
|
||||
async function waitForDns(hostname, zoneName, type, value, options) {
|
||||
assert.strictEqual(typeof hostname, 'string');
|
||||
assert.strictEqual(typeof zoneName, 'string');
|
||||
assert(type === 'A' || type === 'AAAA' || type === 'TXT');
|
||||
assert.strictEqual(typeof value, 'string');
|
||||
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
debug('waitForDns: hostname %s to be %s in zone %s.', hostname, value, zoneName);
|
||||
debug(`waitForDns: ${hostname} to be ${value} in zone ${zoneName}`);
|
||||
|
||||
var attempt = 0;
|
||||
async.retry(options, function (retryCallback) {
|
||||
++attempt;
|
||||
debug(`waitForDns (try ${attempt}): ${hostname} to be ${value} in zone ${zoneName}`);
|
||||
await promiseRetry(Object.assign({ debug }, options), async function () {
|
||||
const nameservers = await dig.resolve(zoneName, 'NS', { timeout: 5000 });
|
||||
if (!nameservers) throw new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to get nameservers');
|
||||
|
||||
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
|
||||
if (error || !nameservers) return retryCallback(error || new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to get nameservers'));
|
||||
|
||||
async.every(nameservers, isChangeSynced.bind(null, hostname, type, value), function (error, synced) {
|
||||
debug('waitForDns: %s %s ns: %j', hostname, synced ? 'done' : 'not done', nameservers);
|
||||
|
||||
retryCallback(synced ? null : new BoxError(BoxError.EXTERNAL_ERROR, 'ETRYAGAIN'));
|
||||
});
|
||||
});
|
||||
}, function retryDone(error) {
|
||||
if (error) return callback(error);
|
||||
|
||||
debug(`waitForDns: ${hostname} has propagated`);
|
||||
|
||||
callback(null);
|
||||
for (const nameserver of nameservers) {
|
||||
const synced = await isChangeSynced(hostname, type, value, nameserver);
|
||||
debug('waitForDns: %s %s ns: %j', hostname, synced ? 'done' : 'not done', nameservers);
|
||||
if (!synced) throw new BoxError(BoxError.EXTERNAL_ERROR, 'ETRYAGAIN');
|
||||
}
|
||||
});
|
||||
|
||||
debug(`waitForDns: ${hostname} has propagated`);
|
||||
}
|
||||
|
||||
+8
-8
@@ -13,6 +13,7 @@ exports = module.exports = {
|
||||
const assert = require('assert'),
|
||||
BoxError = require('../boxerror.js'),
|
||||
debug = require('debug')('box:dns/manual'),
|
||||
dig = require('../dig.js'),
|
||||
dns = require('../dns.js'),
|
||||
safe = require('safetydance'),
|
||||
settings = require('../settings.js'),
|
||||
@@ -58,17 +59,16 @@ function del(domainObject, location, type, values, callback) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
function wait(domainObject, location, type, value, options, callback) {
|
||||
async function wait(domainObject, subdomain, type, value, options) {
|
||||
assert.strictEqual(typeof domainObject, 'object');
|
||||
assert.strictEqual(typeof location, 'string');
|
||||
assert.strictEqual(typeof subdomain, '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');
|
||||
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
const fqdn = dns.fqdn(subdomain, domainObject);
|
||||
|
||||
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
|
||||
await waitForDns(fqdn, domainObject.zoneName, type, value, options);
|
||||
}
|
||||
|
||||
async function verifyDomainConfig(domainObject) {
|
||||
@@ -76,14 +76,14 @@ async function verifyDomainConfig(domainObject) {
|
||||
|
||||
const zoneName = domainObject.zoneName;
|
||||
|
||||
const [error, nameservers] = await safe(dns.promises.resolve(zoneName, 'NS', { timeout: 5000 }));
|
||||
const [error, nameservers] = await safe(dig.resolve(zoneName, 'NS', { timeout: 5000 }));
|
||||
if (error && error.code === 'ENOTFOUND') throw new BoxError(BoxError.BAD_FIELD, 'Unable to resolve nameservers for this domain', { field: 'nameservers' });
|
||||
if (error || !nameservers) throw new BoxError(BoxError.BAD_FIELD, error ? error.message : 'Unable to get nameservers', { field: 'nameservers' });
|
||||
|
||||
const location = 'cloudrontestdns';
|
||||
const fqdn = dns.fqdn(location, domainObject);
|
||||
|
||||
const [ipv4Error, ipv4Result] = await safe(dns.promises.resolve(fqdn, 'A', { server: '127.0.0.1', timeout: 5000 }));
|
||||
const [ipv4Error, ipv4Result] = await safe(dig.resolve(fqdn, 'A', { server: '127.0.0.1', timeout: 5000 }));
|
||||
if (ipv4Error && ipv4Error.code === 'ENOTFOUND') throw new BoxError(BoxError.BAD_FIELD, `Unable to resolve IPv4 of ${fqdn}`, { field: 'nameservers' });
|
||||
if (ipv4Error || !ipv4Result) throw new BoxError(BoxError.BAD_FIELD, ipv4Error ? ipv4Error.message : `Unable to resolve IPv4 of ${fqdn}`, { field: 'nameservers' });
|
||||
|
||||
@@ -92,7 +92,7 @@ async function verifyDomainConfig(domainObject) {
|
||||
|
||||
const ipv6Enabled = await settings.getIPv6Config();
|
||||
if (ipv6Enabled) {
|
||||
const [ipv6Error, ipv6Result] = await safe(dns.promises.resolve(fqdn, 'AAAA', { server: '127.0.0.1', timeout: 5000 }));
|
||||
const [ipv6Error, ipv6Result] = await safe(dig.resolve(fqdn, 'AAAA', { server: '127.0.0.1', timeout: 5000 }));
|
||||
if (ipv6Error && ipv6Error.code === 'ENOTFOUND') throw new BoxError(BoxError.BAD_FIELD, `Unable to resolve IPv6 of ${fqdn}`, { field: 'nameservers' });
|
||||
if (ipv6Error || !ipv6Result) throw new BoxError(BoxError.BAD_FIELD, ipv6Error ? ipv6Error.message : `Unable to resolve IPv6 of ${fqdn}`, { field: 'nameservers' });
|
||||
|
||||
|
||||
+9
-8
@@ -77,6 +77,7 @@ const assert = require('assert'),
|
||||
crypto = require('crypto'),
|
||||
database = require('./database.js'),
|
||||
debug = require('debug')('box:mail'),
|
||||
dig = require('./dig.js'),
|
||||
dns = require('./dns.js'),
|
||||
docker = require('./docker.js'),
|
||||
domains = require('./domains.js'),
|
||||
@@ -267,7 +268,7 @@ async function checkDkim(mailDomain) {
|
||||
|
||||
dkim.expected = `v=DKIM1; t=s; p=${publicKey}`;
|
||||
|
||||
const [error, txtRecords] = await safe(dns.promises.resolve(dkim.domain, dkim.type, DNS_OPTIONS));
|
||||
const [error, txtRecords] = await safe(dig.resolve(dkim.domain, dkim.type, DNS_OPTIONS));
|
||||
if (error) {
|
||||
dkim.errorMessage = error.message;
|
||||
return dkim;
|
||||
@@ -296,7 +297,7 @@ async function checkSpf(domain, mailFqdn) {
|
||||
errorMessage: ''
|
||||
};
|
||||
|
||||
const [error, txtRecords] = await safe(dns.promises.resolve(spf.domain, spf.type, DNS_OPTIONS));
|
||||
const [error, txtRecords] = await safe(dig.resolve(spf.domain, spf.type, DNS_OPTIONS));
|
||||
if (error) {
|
||||
spf.errorMessage = error.message;
|
||||
return spf;
|
||||
@@ -334,7 +335,7 @@ async function checkMx(domain, mailFqdn) {
|
||||
errorMessage: ''
|
||||
};
|
||||
|
||||
const [error, mxRecords] = await safe(dns.promises.resolve(mx.domain, mx.type, DNS_OPTIONS));
|
||||
const [error, mxRecords] = await safe(dig.resolve(mx.domain, mx.type, DNS_OPTIONS));
|
||||
if (error) {
|
||||
mx.errorMessage = error.message;
|
||||
return mx;
|
||||
@@ -347,7 +348,7 @@ async function checkMx(domain, mailFqdn) {
|
||||
if (mx.status) return mx; // MX record is "my."
|
||||
|
||||
// cloudflare might create a conflict subdomain (https://support.cloudflare.com/hc/en-us/articles/360020296512-DNS-Troubleshooting-FAQ)
|
||||
const [error2, mxIps] = await safe(dns.promises.resolve(mxRecords[0].exchange, 'A', DNS_OPTIONS));
|
||||
const [error2, mxIps] = await safe(dig.resolve(mxRecords[0].exchange, 'A', DNS_OPTIONS));
|
||||
if (error2 || mxIps.length !== 1) return mx;
|
||||
|
||||
const [error3, ip] = await safe(sysinfo.getServerIPv4());
|
||||
@@ -379,7 +380,7 @@ async function checkDmarc(domain) {
|
||||
errorMessage: ''
|
||||
};
|
||||
|
||||
const [error, txtRecords] = await safe(dns.promises.resolve(dmarc.domain, dmarc.type, DNS_OPTIONS));
|
||||
const [error, txtRecords] = await safe(dig.resolve(dmarc.domain, dmarc.type, DNS_OPTIONS));
|
||||
|
||||
if (error) {
|
||||
dmarc.errorMessage = error.message;
|
||||
@@ -417,7 +418,7 @@ async function checkPtr(mailFqdn) {
|
||||
ptr.domain = ip.split('.').reverse().join('.') + '.in-addr.arpa';
|
||||
ptr.name = ip;
|
||||
|
||||
const [error2, ptrRecords] = await safe(dns.promises.resolve(ptr.domain, 'PTR', DNS_OPTIONS));
|
||||
const [error2, ptrRecords] = await safe(dig.resolve(ptr.domain, 'PTR', DNS_OPTIONS));
|
||||
if (error2) {
|
||||
ptr.errorMessage = error2.message;
|
||||
return ptr;
|
||||
@@ -500,14 +501,14 @@ async function checkRblStatus(domain) {
|
||||
// https://tools.ietf.org/html/rfc5782
|
||||
const blacklistedServers = [];
|
||||
for (const rblServer of RBL_LIST) {
|
||||
const [error, records] = await safe(dns.promises.resolve(flippedIp + '.' + rblServer.dns, 'A', DNS_OPTIONS));
|
||||
const [error, records] = await safe(dig.resolve(flippedIp + '.' + rblServer.dns, 'A', DNS_OPTIONS));
|
||||
if (error || !records) continue; // not listed
|
||||
|
||||
debug(`checkRblStatus: ${domain} (ip: ${flippedIp}) is in the blacklist of ${JSON.stringify(rblServer)}`);
|
||||
|
||||
const result = _.extend({ }, rblServer);
|
||||
|
||||
const [error2, txtRecords] = await safe(dns.promises.resolve(flippedIp + '.' + rblServer.dns, 'TXT', DNS_OPTIONS));
|
||||
const [error2, txtRecords] = await safe(dig.resolve(flippedIp + '.' + rblServer.dns, 'TXT', DNS_OPTIONS));
|
||||
result.txtRecords = error2 || !txtRecords ? 'No txt record' : txtRecords.map(x => x.join(''));
|
||||
|
||||
debug(`checkRblStatus: ${domain} (error: ${error2.message}) (txtRecords: ${JSON.stringify(txtRecords)})`);
|
||||
|
||||
@@ -54,11 +54,11 @@ describe('Mail API', function () {
|
||||
let dkimDomain, spfDomain, mxDomain, dmarcDomain;
|
||||
|
||||
before(function (done) {
|
||||
const dns = require('../../dns.js');
|
||||
const dig = require('../../dig.js');
|
||||
|
||||
// replace dns resolveTxt()
|
||||
resolve = dns.promises.resolve;
|
||||
dns.promises.resolve = async function (hostname, type/*, options*/) {
|
||||
resolve = dig.resolve;
|
||||
dig.resolve = async function (hostname, type/*, options*/) {
|
||||
expect(hostname).to.be.a('string');
|
||||
|
||||
if (!dnsAnswerQueue[hostname] || !(type in dnsAnswerQueue[hostname])) throw new Error('no mock answer');
|
||||
@@ -84,9 +84,9 @@ describe('Mail API', function () {
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
const dns = require('../../dns.js');
|
||||
const dig = require('../../dig.js');
|
||||
|
||||
dns.promises.resolve = resolve;
|
||||
dig.resolve = resolve;
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user