diff --git a/src/mail.js b/src/mail.js index 3e5b585b6..4f0450c4e 100644 --- a/src/mail.js +++ b/src/mail.js @@ -212,22 +212,25 @@ async function listDomains() { return results; } -async function checkOutboundPort25() { - return await new Promise((resolve) => { +async function checkOutboundPort25(family) { + const ip = family === 4 ? await network.getIPv4() : await network.getIPv6(); + if (ip === null) return; // ipv4/ipv6 is disabled + + return await new Promise((resolve, reject) => { 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.connect({ port: 25, host: constants.PORT25_CHECK_SERVER, family }); // family is 4 to keep it predictable client.on('connect', function () { client.destroy(); // do not use end() because it still triggers timeout - resolve({ status: 'passed', message: 'Port 25 (outbound) is unblocked' }); + resolve(); }); client.on('timeout', function () { client.destroy(); - resolve({ status: 'failed', message: `Connect to ${constants.PORT25_CHECK_SERVER} timed out. Check if port 25 (outbound) is blocked` }); + reject(new Error(`IPv${family} connect to ${constants.PORT25_CHECK_SERVER} timed out`)); }); client.on('error', function (error) { client.destroy(); - resolve({ status: 'failed', message: `Connect to ${constants.PORT25_CHECK_SERVER} failed: ${error.message}. Check if port 25 (outbound) is blocked` }); + reject(new Error(`IPv${family} connect to ${constants.PORT25_CHECK_SERVER} failed: ${error.message}`)); }); }); } @@ -236,7 +239,15 @@ async function checkSmtpRelay(relay) { assert.strictEqual(typeof relay, 'object'); if (relay.provider === 'noop') return { status: 'skipped', message: 'Outbound disabled' }; - if (relay.provider === 'cloudron-smtp') return await checkOutboundPort25(); + if (relay.provider === 'cloudron-smtp') { + const results = await Promise.allSettled([ checkOutboundPort25(4), checkOutboundPort25(6) ]); + if (results[0].status === 'fulfilled' && results[1].status === 'fulfilled') return { status: 'passed', message: 'Port 25 (outbound) is unblocked' }; + const messages = []; + if (results[0].status === 'rejected') messages.push(results[0].reason.message); + if (results[1].status === 'rejected') messages.push(results[1].reason.message); + messages.push('Check if port 25 (outbound) is blocked.'); + return { status: 'failed', message: messages.join('. ') }; + } const options = { connectionTimeout: 5000,