mail: make status a tristate
status can be 'passed', 'failed' or 'skipped'. The motivation for this change is that when using a relay, we can provide a message indicating why the check was skipped.
This commit is contained in:
+145
-194
@@ -171,45 +171,30 @@ function validateDisplayName(name) {
|
||||
}
|
||||
|
||||
async function checkOutboundPort25() {
|
||||
const relay = {
|
||||
value: 'OK',
|
||||
status: false,
|
||||
errorMessage: ''
|
||||
};
|
||||
|
||||
return await new Promise((resolve) => {
|
||||
const client = new net.Socket();
|
||||
client.setTimeout(5000);
|
||||
client.connect({ port: 25, host: constants.PORT25_CHECK_SERVER, family: 4 }); // family is 4 to keep it predictable
|
||||
client.on('connect', function () {
|
||||
relay.status = true;
|
||||
relay.value = 'OK';
|
||||
client.destroy(); // do not use end() because it still triggers timeout
|
||||
resolve(relay);
|
||||
resolve({ status: 'passed', message: 'Port 25 (outbound) is unblocked' });
|
||||
});
|
||||
client.on('timeout', function () {
|
||||
relay.status = false;
|
||||
relay.value = `Connect to ${constants.PORT25_CHECK_SERVER} timed out. Check if port 25 (outbound) is blocked`;
|
||||
client.destroy();
|
||||
relay.errorMessage = `Connect to ${constants.PORT25_CHECK_SERVER} timed out.`;
|
||||
resolve(relay);
|
||||
resolve({ status: 'failed', message: `Connect to ${constants.PORT25_CHECK_SERVER} timed out. Check if port 25 (outbound) is blocked` });
|
||||
});
|
||||
client.on('error', function (error) {
|
||||
relay.status = false;
|
||||
relay.value = `Connect to ${constants.PORT25_CHECK_SERVER} failed: ${error.message}. Check if port 25 (outbound) is blocked`;
|
||||
client.destroy();
|
||||
relay.errorMessage = `Connect to ${constants.PORT25_CHECK_SERVER} failed: ${error.message}`;
|
||||
resolve(relay);
|
||||
resolve({ status: 'failed', message: `Connect to ${constants.PORT25_CHECK_SERVER} failed: ${error.message}. Check if port 25 (outbound) is blocked` });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function checkSmtpRelay(relay) {
|
||||
const result = {
|
||||
value: 'OK',
|
||||
status: false,
|
||||
errorMessage: ''
|
||||
};
|
||||
assert.strictEqual(typeof relay, 'object');
|
||||
|
||||
if (relay.provider === 'noop') return { status: 'skipped', message: 'Outbound disabled' };
|
||||
if (relay.provider === 'cloudron-smtp') return await checkOutboundPort25();
|
||||
|
||||
const options = {
|
||||
connectionTimeout: 5000,
|
||||
@@ -233,133 +218,122 @@ async function checkSmtpRelay(relay) {
|
||||
const transporter = nodemailer.createTransport(options);
|
||||
|
||||
const [error] = await safe(transporter.verify());
|
||||
result.status = !error;
|
||||
if (error) {
|
||||
result.value = result.errorMessage = error.message;
|
||||
return result;
|
||||
}
|
||||
const result = {
|
||||
status: error ? 'failed' : 'passed',
|
||||
message: error ? error.message : `Connection to ${relay.host}:${relay.port} succeeded`
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function verifyRelay(relay) {
|
||||
assert.strictEqual(typeof relay, 'object');
|
||||
|
||||
// we used to verify cloudron-smtp with checkOutboundPort25 but that is unreliable given that we just
|
||||
// randomly select some smtp server
|
||||
if (relay.provider === 'cloudron-smtp' || relay.provider === 'noop') return null;
|
||||
|
||||
const result = await checkSmtpRelay(relay);
|
||||
if (result.errorMessage) return new BoxError(BoxError.BAD_FIELD, result.errorMessage);
|
||||
}
|
||||
|
||||
async function checkDkim(mailDomain) {
|
||||
assert.strictEqual(typeof mailDomain, 'object');
|
||||
|
||||
const domain = mailDomain.domain;
|
||||
const dkim = {
|
||||
if (mailDomain.relay.provider === 'noop') return { status: 'skipped', message: 'Outbound disabled' };
|
||||
if (mailDomain.relay.provider !== 'cloudron-smtp') return { status: 'skipped', message: 'DKIM check skipped, email is sent through a relay service' };
|
||||
|
||||
const { domain } = mailDomain;
|
||||
|
||||
const result = {
|
||||
domain: `${mailDomain.dkimSelector}._domainkey.${domain}`,
|
||||
name: `${mailDomain.dkimSelector}._domainkey`,
|
||||
type: 'TXT',
|
||||
expected: null,
|
||||
value: null,
|
||||
status: false,
|
||||
errorMessage: ''
|
||||
status: 'failed',
|
||||
message: ''
|
||||
};
|
||||
|
||||
const publicKey = mailDomain.dkimKey.publicKey.split('\n').slice(1, -2).join(''); // remove header, footer and new lines
|
||||
|
||||
dkim.expected = `v=DKIM1; t=s; p=${publicKey}`;
|
||||
result.expected = `v=DKIM1; t=s; p=${publicKey}`;
|
||||
|
||||
const [error, txtRecords] = await safe(dig.resolve(dkim.domain, dkim.type, DNS_OPTIONS));
|
||||
if (error) {
|
||||
dkim.errorMessage = error.message;
|
||||
return dkim;
|
||||
}
|
||||
const [error, txtRecords] = await safe(dig.resolve(result.domain, result.type, DNS_OPTIONS));
|
||||
if (error) return Object.assign(result, { status: 'failed', message: error.message });
|
||||
if (txtRecords.length === 0) return Object.assign(result, { status: 'failed', message: 'No DKIM record' });
|
||||
|
||||
if (txtRecords.length !== 0) {
|
||||
dkim.value = txtRecords[0].join('');
|
||||
const actual = txtToDict(dkim.value);
|
||||
dkim.status = actual.p === publicKey;
|
||||
}
|
||||
result.value = txtRecords[0].join('');
|
||||
const actual = txtToDict(result.value);
|
||||
result.status = actual.p === publicKey ? 'passed' : 'failed';
|
||||
|
||||
return dkim;
|
||||
return result;
|
||||
}
|
||||
|
||||
async function checkSpf(domain, mailFqdn) {
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
async function checkSpf(mailDomain, mailFqdn) {
|
||||
assert.strictEqual(typeof mailDomain, 'object');
|
||||
assert.strictEqual(typeof mailFqdn, 'string');
|
||||
|
||||
const spf = {
|
||||
domain,
|
||||
if (mailDomain.relay.provider === 'noop') return { status: 'skipped', message: 'Outbound disabled' };
|
||||
if (mailDomain.relay.provider !== 'cloudron-smtp') return { status: 'skipped', message: 'SPF check skipped. Please check that the relay provider has correct SPF settings for this domain' };
|
||||
|
||||
const result = {
|
||||
domain: mailDomain.domain,
|
||||
name: '@',
|
||||
type: 'TXT',
|
||||
value: null,
|
||||
expected: `v=spf1 a:${mailFqdn} ~all`,
|
||||
status: false,
|
||||
errorMessage: ''
|
||||
status: 'failed',
|
||||
message: ''
|
||||
};
|
||||
|
||||
const [error, txtRecords] = await safe(dig.resolve(spf.domain, spf.type, DNS_OPTIONS));
|
||||
if (error) {
|
||||
spf.errorMessage = error.message;
|
||||
return spf;
|
||||
}
|
||||
const [error, txtRecords] = await safe(dig.resolve(result.domain, result.type, DNS_OPTIONS));
|
||||
if (error) return Object.assign(result, { message: error.message });
|
||||
|
||||
let i;
|
||||
for (i = 0; i < txtRecords.length; i++) {
|
||||
const txtRecord = txtRecords[i].join(''); // https://agari.zendesk.com/hc/en-us/articles/202952749-How-long-can-my-SPF-record-be-
|
||||
if (txtRecord.indexOf('v=spf1 ') !== 0) continue; // not SPF
|
||||
spf.value = txtRecord;
|
||||
spf.status = spf.value.indexOf(` a:${mailFqdn}`) !== -1;
|
||||
result.value = txtRecord;
|
||||
result.status = result.value.indexOf(` a:${mailFqdn}`) !== -1 ? 'passed' : 'failed';
|
||||
break;
|
||||
}
|
||||
|
||||
if (spf.status) {
|
||||
spf.expected = spf.value;
|
||||
if (result.status === 'passed') {
|
||||
result.expected = result.value;
|
||||
} else if (i !== txtRecords.length) {
|
||||
spf.expected = `v=spf1 a:${mailFqdn} ` + spf.value.slice('v=spf1 '.length);
|
||||
result.expected = `v=spf1 a:${mailFqdn} ` + result.value.slice('v=spf1 '.length);
|
||||
}
|
||||
|
||||
return spf;
|
||||
return result;
|
||||
}
|
||||
|
||||
async function checkMx(domain, mailFqdn) {
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
async function checkMx(mailDomain, mailFqdn) {
|
||||
assert.strictEqual(typeof mailDomain, 'object');
|
||||
assert.strictEqual(typeof mailFqdn, 'string');
|
||||
|
||||
const mx = {
|
||||
if (!mailDomain.enabled) return { status: 'skipped', message: 'MX check skipped, server does not handle incoming email for this domain' };
|
||||
|
||||
const { domain } = mailDomain;
|
||||
|
||||
const result = {
|
||||
domain,
|
||||
name: '@',
|
||||
type: 'MX',
|
||||
value: null,
|
||||
expected: `10 ${mailFqdn}.`,
|
||||
status: false,
|
||||
errorMessage: ''
|
||||
status: 'failed',
|
||||
message: ''
|
||||
};
|
||||
|
||||
const [error, mxRecords] = await safe(dig.resolve(mx.domain, mx.type, DNS_OPTIONS));
|
||||
if (error) {
|
||||
mx.errorMessage = error.message;
|
||||
return mx;
|
||||
}
|
||||
if (mxRecords.length === 0) return mx;
|
||||
const [error, mxRecords] = await safe(dig.resolve(result.domain, result.type, DNS_OPTIONS));
|
||||
if (error) return Object.assign(result, { status: 'failed', message: error.message });
|
||||
if (mxRecords.length === 0) return Object.assign(result, { status: 'failed', message: 'No MX record' });
|
||||
|
||||
mx.status = mxRecords.some(mx => mx.exchange === mailFqdn); // this lets use change priority and/or setup backup MX
|
||||
mx.value = mxRecords.map(function (r) { return r.priority + ' ' + r.exchange + '.'; }).join(' ');
|
||||
result.status = mxRecords.some(mx => mx.exchange === mailFqdn) ? 'passed' : 'failed'; // this lets use change priority and/or setup backup MX
|
||||
result.value = mxRecords.map(function (r) { return r.priority + ' ' + r.exchange + '.'; }).join(' ');
|
||||
|
||||
if (mx.status) return mx; // MX record is "my."
|
||||
if (result.status === 'passed') return result; // 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(dig.resolve(mxRecords[0].exchange, 'A', DNS_OPTIONS));
|
||||
if (error2 || mxIps.length !== 1) return mx;
|
||||
if (error2 || mxIps.length !== 1) return result;
|
||||
|
||||
const [error3, ip] = await safe(network.getIPv4());
|
||||
if (error3) return mx;
|
||||
if (error3) return result;
|
||||
|
||||
mx.status = mxIps[0] === ip;
|
||||
result.status = mxIps[0] === ip ? 'passed' : 'failed';
|
||||
|
||||
return mx;
|
||||
return result;
|
||||
}
|
||||
|
||||
function txtToDict(txt) {
|
||||
@@ -371,57 +345,54 @@ function txtToDict(txt) {
|
||||
return dict;
|
||||
}
|
||||
|
||||
async function checkDmarc(domain) {
|
||||
async function checkDmarc(mailDomain) {
|
||||
assert.strictEqual(typeof mailDomain, 'object');
|
||||
|
||||
const dmarc = {
|
||||
if (!mailDomain.enabled) return { status: 'skipped', message: 'DMARC check skipped, server does not handle incoming email for this domain' };
|
||||
|
||||
const { domain } = mailDomain;
|
||||
|
||||
const result = {
|
||||
domain: `_dmarc.${domain}`,
|
||||
name: '_dmarc',
|
||||
type: 'TXT',
|
||||
value: null,
|
||||
expected: 'v=DMARC1; p=reject; pct=100',
|
||||
status: false,
|
||||
errorMessage: ''
|
||||
status: 'failed',
|
||||
message: ''
|
||||
};
|
||||
|
||||
const [error, txtRecords] = await safe(dig.resolve(dmarc.domain, dmarc.type, DNS_OPTIONS));
|
||||
const [error, txtRecords] = await safe(dig.resolve(result.domain, result.type, DNS_OPTIONS));
|
||||
if (error) return Object.assign(result, { status: 'failed', message: error.message });
|
||||
if (txtRecords.length === 0) return Object.assign(result, { status: 'failed', message: 'No DMARC records' });
|
||||
|
||||
if (error) {
|
||||
dmarc.errorMessage = error.message;
|
||||
return dmarc;
|
||||
}
|
||||
result.value = txtRecords[0].join('');
|
||||
const actual = txtToDict(result.value);
|
||||
result.status = actual.v === 'DMARC1' ? 'passed' : 'failed'; // see box#666
|
||||
|
||||
if (txtRecords.length !== 0) {
|
||||
dmarc.value = txtRecords[0].join('');
|
||||
const actual = txtToDict(dmarc.value);
|
||||
dmarc.status = actual.v === 'DMARC1'; // see box#666
|
||||
}
|
||||
|
||||
return dmarc;
|
||||
return result;
|
||||
}
|
||||
|
||||
async function checkPtr6(mailFqdn) {
|
||||
async function checkPtr6(mailDomain, mailFqdn) {
|
||||
assert.strictEqual(typeof mailDomain, 'object');
|
||||
assert.strictEqual(typeof mailFqdn, 'string');
|
||||
|
||||
const ptr = {
|
||||
if (mailDomain.relay.provider === 'noop') return { status: 'skipped', message: 'Outbound disabled' };
|
||||
if (mailDomain.relay.provider !== 'cloudron-smtp') return { status: 'skipped', message: 'PTR6 check was skipped, email is sent through a relay service' };
|
||||
|
||||
const result = {
|
||||
domain: null,
|
||||
name: null,
|
||||
type: 'PTR',
|
||||
value: null,
|
||||
expected: mailFqdn, // any trailing '.' is added by client software (https://lists.gt.net/spf/devel/7918)
|
||||
status: false,
|
||||
errorMessage: ''
|
||||
status: 'failed',
|
||||
message: ''
|
||||
};
|
||||
|
||||
const [error, ip] = await safe(network.getIPv6());
|
||||
if (error) {
|
||||
ptr.errorMessage = error.message;
|
||||
return ptr;
|
||||
}
|
||||
if (ip === null) {
|
||||
ptr.status = true;
|
||||
ptr.expected = 'Check skipped, server has no IPv6';
|
||||
return ptr;
|
||||
}
|
||||
if (error) return Object.assign(result, { status: 'failed', message: error.message });
|
||||
if (ip === null) return Object.assign(result, { status: 'skipped', message: 'PTR6 check was skipped, server has no IPv6' });
|
||||
|
||||
function expandIPv6(ipv6) {
|
||||
const parts = ipv6.split('::');
|
||||
@@ -436,65 +407,54 @@ async function checkPtr6(mailFqdn) {
|
||||
const reversed = expanded.split('').reverse().join('');
|
||||
const reversedWithDots = reversed.split('').join('.');
|
||||
|
||||
ptr.domain = `${reversedWithDots}.ip6.arpa`;
|
||||
ptr.name = ip;
|
||||
result.domain = `${reversedWithDots}.ip6.arpa`;
|
||||
result.name = ip;
|
||||
|
||||
const [error2, ptrRecords] = await safe(dig.resolve(ptr.domain, 'PTR', DNS_OPTIONS));
|
||||
if (error2) {
|
||||
ptr.errorMessage = error2.message;
|
||||
return ptr;
|
||||
}
|
||||
const [error2, ptrRecords] = await safe(dig.resolve(result.domain, 'PTR', DNS_OPTIONS));
|
||||
if (error2) return Object.assign(result, { status: 'failed', message: error2.message });
|
||||
if (ptrRecords.length === 0) return Object.assign(result, { status: 'failed', message: 'No PTR6 record' });
|
||||
|
||||
if (ptrRecords.length !== 0) {
|
||||
ptr.value = ptrRecords.join(' ');
|
||||
ptr.status = ptrRecords.some(function (v) { return v === ptr.expected; });
|
||||
}
|
||||
result.value = ptrRecords.join(' ');
|
||||
result.status = ptrRecords.some(function (v) { return v === result.expected; }) ? 'passed' : 'failed';
|
||||
|
||||
return ptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
async function checkPtr4(mailFqdn) {
|
||||
async function checkPtr4(mailDomain, mailFqdn) {
|
||||
assert.strictEqual(typeof mailDomain, 'object');
|
||||
assert.strictEqual(typeof mailFqdn, 'string');
|
||||
|
||||
const ptr = {
|
||||
if (mailDomain.relay.provider === 'noop') return { status: 'skipped', message: 'Outbound disabled' };
|
||||
if (mailDomain.relay.provider !== 'cloudron-smtp') return { status: 'skipped', message: 'PTR4 check was skipped, email is sent through a relay service' };
|
||||
|
||||
const result = {
|
||||
domain: null,
|
||||
name: null,
|
||||
type: 'PTR',
|
||||
value: null,
|
||||
expected: mailFqdn, // any trailing '.' is added by client software (https://lists.gt.net/spf/devel/7918)
|
||||
status: false,
|
||||
errorMessage: ''
|
||||
status: 'failed',
|
||||
message: ''
|
||||
};
|
||||
|
||||
const [error, ip] = await safe(network.getIPv4());
|
||||
if (error) {
|
||||
ptr.errorMessage = error.message;
|
||||
return ptr;
|
||||
}
|
||||
if (ip === null) {
|
||||
ptr.status = true;
|
||||
ptr.expected = 'Check skipped, server has no IPv4';
|
||||
return ptr;
|
||||
}
|
||||
if (error) return Object.assign(result, { status: 'failed', message: error.message });
|
||||
if (ip === null) return Object.assign(result, { status: 'skipped', message: 'PTR4 check was skipped, server has no IPv4' });
|
||||
|
||||
ptr.domain = ip.split('.').reverse().join('.') + '.in-addr.arpa';
|
||||
ptr.name = ip;
|
||||
result.domain = ip.split('.').reverse().join('.') + '.in-addr.arpa';
|
||||
result.name = ip;
|
||||
|
||||
const [error2, ptrRecords] = await safe(dig.resolve(ptr.domain, 'PTR', DNS_OPTIONS));
|
||||
if (error2) {
|
||||
ptr.errorMessage = error2.message;
|
||||
return ptr;
|
||||
}
|
||||
const [error2, ptrRecords] = await safe(dig.resolve(result.domain, 'PTR', DNS_OPTIONS));
|
||||
if (error2) return Object.assign(result, { status: 'failed', message: error2.message });
|
||||
if (ptrRecords.length === 0) return Object.assign(result, { status: 'failed', message: 'No PTR4 record' });
|
||||
|
||||
if (ptrRecords.length !== 0) {
|
||||
ptr.value = ptrRecords.join(' ');
|
||||
ptr.status = ptrRecords.some(function (v) { return v === ptr.expected; });
|
||||
}
|
||||
result.value = ptrRecords.join(' ');
|
||||
result.status = ptrRecords.some(function (v) { return v === result.expected; }) ? 'passed' : 'failed';
|
||||
|
||||
return ptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
// https://raw.githubusercontent.com/jawsome/node-dnsbl/master/list.json
|
||||
// https://raw.githubusercontent.com/jawsome/node-dnsbl/master/list.json https://multirbl.valli.org/list/
|
||||
const RBL_LIST = [
|
||||
{
|
||||
'name': 'Abuse.ch',
|
||||
@@ -550,12 +510,17 @@ const RBL_LIST = [
|
||||
];
|
||||
|
||||
// this function currently only looks for black lists based on IP. TODO: also look up by domain
|
||||
async function checkRblStatus(domain) {
|
||||
async function checkRbl4(mailDomain) {
|
||||
assert.strictEqual(typeof mailDomain, 'object');
|
||||
|
||||
if (mailDomain.relay.provider === 'noop') return { status: 'skipped', message: 'Outbound disabled' };
|
||||
if (mailDomain.relay.provider !== 'cloudron-smtp') return { status: 'skipped', message: 'RBL check was skipped, email is sent through a relay service' };
|
||||
|
||||
const { domain } = mailDomain;
|
||||
|
||||
const [error, ip] = await safe(network.getIPv4());
|
||||
if (error) {
|
||||
debug(`checkRblStatus: unable to determine server IPv4: ${error.message}`);
|
||||
return { status: false, ip: null, servers: [] };
|
||||
}
|
||||
if (error) return { status: 'failed', ip: null, servers: [], message: `Unable to determine server IPv4: ${error.message}` };
|
||||
if (ip === null) return { status: 'skipped', ip: null, servers: [], message: 'RBL check was skipped, server has no IPv4' };
|
||||
|
||||
const flippedIp = ip.split('.').reverse().join('.');
|
||||
|
||||
@@ -579,48 +544,34 @@ async function checkRblStatus(domain) {
|
||||
|
||||
debug(`checkRblStatus: ${domain} (ip: ${ip}) blockedServers: ${JSON.stringify(blockedServers)})`);
|
||||
|
||||
return { status: blockedServers.length === 0, ip, servers: blockedServers };
|
||||
return { status: blockedServers.length === 0 ? 'passed' : 'failed', ip, servers: blockedServers };
|
||||
}
|
||||
|
||||
async function getStatus(domain) {
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
|
||||
// ensure we always have a valid toplevel properties for the api
|
||||
const results = {
|
||||
dns: {}, // { mx/dmarc/dkim/spf/ptr: { expected, value, name, domain, type, status } }
|
||||
rbl: {}, // { status, ip, servers: [{name,site,dns}]} optional. only for cloudron-smtp
|
||||
relay: {} // { status, value } always checked
|
||||
};
|
||||
|
||||
const { fqdn } = await mailServer.getLocation();
|
||||
|
||||
const mailDomain = await getDomain(domain);
|
||||
if (!mailDomain) throw new BoxError(BoxError.NOT_FOUND, 'mail domain not found');
|
||||
|
||||
const checks = [];
|
||||
if (mailDomain.enabled) {
|
||||
checks.push(
|
||||
{ what: 'dns.mx', promise: checkMx(domain, fqdn) },
|
||||
{ what: 'dns.dmarc', promise: checkDmarc(domain) }
|
||||
);
|
||||
}
|
||||
// mx/dmarc/dkim/spf/ptr: { expected, value, name, domain, type, status } }
|
||||
// rbl4: { status, ip, servers: [{name,site,dns}]}
|
||||
// relay: { status, message } always checked
|
||||
const results = {};
|
||||
|
||||
if (mailDomain.relay.provider === 'cloudron-smtp') {
|
||||
// these tests currently only make sense when using Cloudron's SMTP server at this point
|
||||
checks.push(
|
||||
{ what: 'dns.spf', promise: checkSpf(domain, fqdn) },
|
||||
{ what: 'dns.dkim', promise: checkDkim(mailDomain) },
|
||||
{ what: 'dns.ptr4', promise: checkPtr4(fqdn) },
|
||||
{ what: 'dns.ptr6', promise: checkPtr6(fqdn) },
|
||||
{ what: 'relay', promise: checkOutboundPort25() },
|
||||
{ what: 'rbl', promise: checkRblStatus(domain) },
|
||||
);
|
||||
} else if (mailDomain.relay.provider !== 'noop') {
|
||||
checks.push({ what: 'relay', promise: checkSmtpRelay(mailDomain.relay) });
|
||||
}
|
||||
const checks = [
|
||||
{ what: 'mx', promise: checkMx(mailDomain, fqdn) },
|
||||
{ what: 'dmarc', promise: checkDmarc(mailDomain) },
|
||||
{ what: 'spf', promise: checkSpf(mailDomain, fqdn) },
|
||||
{ what: 'dkim', promise: checkDkim(mailDomain) },
|
||||
{ what: 'ptr4', promise: checkPtr4(mailDomain, fqdn) },
|
||||
{ what: 'ptr6', promise: checkPtr6(mailDomain, fqdn) },
|
||||
{ what: 'rbl4', promise: checkRbl4(mailDomain) },
|
||||
{ what: 'relay', promise: checkSmtpRelay(mailDomain.relay) }
|
||||
];
|
||||
|
||||
// wait for all the checks and record the result
|
||||
const responses = await Promise.allSettled(checks.map(c => c.promise));
|
||||
const responses = await Promise.allSettled(checks.map(c => c.promise)); // wait for all the checks and record the result
|
||||
for (let i = 0; i < checks.length; i++) {
|
||||
const response = responses[i], check = checks[i];
|
||||
if (response.status !== 'fulfilled') {
|
||||
@@ -628,7 +579,7 @@ async function getStatus(domain) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (response.value.errorMessage) debug(`Ignored error - ${check.what} : ${response.value.errorMessage}`);
|
||||
if (response.value.message) debug(`${check.what} : ${response.value.message}`);
|
||||
safe.set(results, checks[i].what, response.value || {});
|
||||
}
|
||||
|
||||
@@ -842,8 +793,8 @@ async function setMailRelay(domain, relay, options) {
|
||||
if (relay.password === constants.SECRET_PLACEHOLDER) relay.password = result.relay.password;
|
||||
|
||||
if (!options.skipVerify) {
|
||||
const error = await verifyRelay(relay);
|
||||
if (error) throw error;
|
||||
const result = await checkSmtpRelay(relay);
|
||||
if (result.status === 'failed') throw new BoxError(BoxError.BAD_FIELD, result.message);
|
||||
}
|
||||
|
||||
await updateDomain(domain, { relay });
|
||||
|
||||
Reference in New Issue
Block a user