Skip to content

Commit

Permalink
feat(autodeploy): Enabled auto-deployment. Updated response errors
Browse files Browse the repository at this point in the history
  • Loading branch information
andris9 committed Jan 19, 2024
1 parent 1a49e4d commit 324c021
Show file tree
Hide file tree
Showing 10 changed files with 6,591 additions and 77 deletions.
4 changes: 4 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# These are supported funding model platforms

github: [andris9] # enable once enrolled
custom: ['https://www.paypal.me/nodemailer']
37 changes: 37 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
on:
push:
branches:
- master

permissions:
contents: write
pull-requests: write
id-token: write

name: release
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v3
id: release
with:
release-type: node
package-name: ${{vars.NPM_MODULE_NAME}}
pull-request-title-pattern: 'chore${scope}: release ${version} [skip-ci]'
# The logic below handles the npm publication:
- uses: actions/checkout@v3
# these if statements ensure that a publication only occurs when
# a new release is created:
if: ${{ steps.release.outputs.release_created }}
- uses: actions/setup-node@v3
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
if: ${{ steps.release.outputs.release_created }}
- run: npm ci
if: ${{ steps.release.outputs.release_created }}
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
if: ${{ steps.release.outputs.release_created }}
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,4 @@ typings/
.next

.DS_Store
package-lock.json

18 changes: 10 additions & 8 deletions lib/format-address.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@ function formatAddress(delivery) {
delivery.decodedDomain = delivery.domain.replace(/^\[(ipv6:)?|\]$/gi, '');
if (!net.isIP(delivery.decodedDomain)) {
// the result does not seem to be either IPv4 or IPv6 address
let err = new Error(delivery.decodedDomain + ' does not appear to be a properly formatted IP address');
err.response = '550 ' + err.message;
err.category = 'compliancy';
return Promise.reject(err);
let error = new Error(delivery.decodedDomain + ' does not appear to be a properly formatted IP address');
error.response = `DNS Error: ${error.message}`;
error.category = 'compliancy';
return Promise.reject(error);
}
if (net.isIPv6(delivery.decodedDomain) && dnsOptions.ignoreIPv6) {
let err = new Error(delivery.decodedDomain + ': Can not send mail to IPv6 addresses');
err.response = '550 ' + err.message;
err.category = 'dns';
return Promise.reject(err);
let error = new Error(
`The Mail Exchange (MX) resolved to an IPv6 address "${delivery.decodedDomain}". However, sending mail to IPv6 addresses is not supported in the current configuration`
);
error.response = `Network error: ${error.message}`;
error.category = 'dns';
return Promise.reject(error);
}
} else {
// decode potential unicode in domain part. If nothing was changed then the domain did not use unicode
Expand Down
59 changes: 31 additions & 28 deletions lib/get-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,21 @@ function getConnection(delivery) {
if (!mxHosts.length) {
if (mxHostsSeen.size) {
// we did have some hosts listed but these were filtered out
let err;
let error;
if (delivery.mxLastError) {
err = delivery.mxLastError;
error = delivery.mxLastError;
} else {
err = new Error('Could not connect to MX of ' + delivery.domain);
err.response = '450 ' + err.message;
err.category = 'connect';
error = new Error(`Connection to the Mail Exchange (MX) server of "${delivery.domain}" failed`);
error.response = `Netowrk error: ${error.message}`;
error.category = 'connect';
error.temporary = true;
}
return reject(err);
return reject(error);
}
let err = new Error('Could not find any MX servers for ' + delivery.domain);
err.response = '550 ' + err.message;
err.category = 'dns';
return reject(err);
let error = new Error(`No Mail Exchange (MX) servers were found for "${delivery.domain}"`);
error.response = `DNS Error: ${error.message}`;
error.category = 'dns';
return reject(error);
}

let dnsOptions = delivery.dnsOptions || {};
Expand All @@ -90,10 +91,11 @@ function getConnection(delivery) {
let tried = 0;
let tryNextMX = () => {
if (tried >= mxHosts.length) {
let err = firstError || new Error('Could not connect to any of the MX servers for ' + delivery.domain);
err.response = err.response || '450 ' + err.message;
err.category = err.category || 'network';
return reject(err);
let error =
firstError || new Error(`Unable to establish a connection with any of the Mail Exchange (MX) servers listed for "${delivery.domain}"`);
error.response = error.response || `Network error: ${error.message}`;
error.category = error.category || 'network';
return reject(error);
}

let mx = mxHosts[tried++];
Expand Down Expand Up @@ -167,13 +169,12 @@ function getConnection(delivery) {
mode: mx.policyMatch.mode,
testing: false
});
let code = 554;
let err = new Error(`MTA-STS policy check failed for ${mx.hostname}[${mx.host}] for ${delivery.domain}`);
err.response = code + ' ' + err.message;
err.category = 'policy';
emitConnectError(err);
let error = new Error(`MTA-STS policy check failed for ${mx.hostname}[${mx.host}] for ${delivery.domain}`);
error.response = `Policy error: ${error.message}`;
error.category = 'policy';
emitConnectError(error);
if (!firstError) {
firstError = err;
firstError = error;
}
return setImmediate(tryNextMX);
}
Expand Down Expand Up @@ -224,12 +225,12 @@ function getConnection(delivery) {
});
socket.once('error', err => {
if (err) {
let code = 450;
err.message = `Network error when connecting to MX server ${mx.hostname}[${mx.host}] for ${delivery.domain}: ${
netErrors[err.code] || netErrors[err.errno] || err.message
}`;
err.response = err.response || code + ' ' + err.message;
err.response = err.response || `Netowrk error: ${err.message}`;
err.category = err.category || 'network';
err.temporary = true;
emitConnectError(err);
if (!firstError) {
firstError = err;
Expand All @@ -247,13 +248,15 @@ function getConnection(delivery) {
clearTimeout(connectTimeout);
if (!connected) {
connected = true;
let code = 450;
let err = new Error(`Connection timed out when connecting to MX server ${mx.hostname}[${mx.host}] for ${delivery.domain}`);
err.response = code + ' ' + err.message;
err.category = 'network';
emitConnectError(err);

let error = new Error(`Connection timed out when connecting to MX server ${mx.hostname}[${mx.host}] for ${delivery.domain}`);
error.response = `Network error: ${err.message}`;
error.category = 'network';
error.temporary = true;

emitConnectError(error);
if (!firstError) {
firstError = err;
firstError = error;
}
return setImmediate(tryNextMX);
}
Expand Down
31 changes: 14 additions & 17 deletions lib/resolve-ip.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,25 @@ function resolveIP(delivery) {
firstError.exchange = ip.exchange;
// This is not ENOTFOUND but an actual error, probably an issue with DNS server
// so we return 4xx
firstError.response =
'450 Failed to resolve IP address for ' +
ip.exchange +
' of the MX server for ' +
delivery.domain +
'. ' +
(dnsErrors[firstError.code] || firstError.message);
firstError.response = `DNS Error: Unable to resolve the IP address for the specified host [${
ip.exchange
}] of the Mail Exchange (MX) server for the domain "${delivery.domain}". ${dnsErrors[firstError.code] || firstError.message}`;
firstError.category = 'dns';
firstError.temporary = true;
}
return false;
}
let invalid = tools.isInvalid(delivery, ip);
if (invalid) {
if (!firstError) {
firstError = new Error(
'Can not send mail to the resolved IP address [' +
ip +
'] of the MX server for ' +
delivery.domain +
(typeof invalid === 'string' ? '. ' + invalid : '')
`Unable to deliver email to the IP address [${ip}] resolved for the Mail Exchange (MX) server of "${delivery.domain}"${
typeof invalid === 'string' ? `. ${invalid}` : ''
}`
);
// Invalid IP, so nothing to do here, return 5xx
firstError.response = '550 ' + firstError.message;
firstError.code = 'InvalidIpAddress';
firstError.response = `DNS Error: ${firstError.message}`;
firstError.category = 'dns';
}
} else {
Expand Down Expand Up @@ -111,10 +107,11 @@ function resolveIP(delivery) {
}

// nothing found, we end up here with ENOTFOUND, so return 5xx
let err = new Error('Could not resolve any IP addresses for the MX server for ' + delivery.domain);
err.response = '550 ' + err.message;
err.category = 'dns';
return reject(err);
let error = new Error(`Failed to resolve any IP addresses for the Mail Exchange (MX) server associated with "${delivery.domain}"`);
error.code = 'ENOTFOUND';
error.response = `DNS Error: ${error.message}`;
error.category = 'dns';
return reject(error);
}
resolve(delivery);
})
Expand Down
39 changes: 24 additions & 15 deletions lib/resolve-mx.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,11 @@ async function resolveMX(delivery) {
if (invalid) {
if (!firstError) {
firstError = new Error(
'Can not send mail to the resolved IP address [' +
ip +
'] of the MX server for ' +
delivery.domain +
(typeof invalid === 'string' ? '. ' + invalid : '')
`Unable to deliver email to the IP address [${ip}] resolved for the Mail Exchange (MX) server of "${delivery.domain}"${
typeof invalid === 'string' ? `. ${invalid}` : ''
}`
);
firstError.response = '550 ' + firstError.message;
firstError.response = `DNS Error: ${firstError.message}`;
firstError.category = 'dns';
}
} else {
Expand Down Expand Up @@ -56,19 +54,24 @@ async function resolveMX(delivery) {

dnsResolve(domain, 'MX', (err, list) => {
if (err && err.code !== 'ENODATA' && err.code !== 'ENOTFOUND') {
err.category = 'dns';
err.message = 'DNS error when resolving MX server for ' + domain + ': ' + (dnsErrors[err.code] || err.message);
err.response = '450 ' + err.message;
err.message = `DNS error occurred while resolving the Mail Exchange (MX) server for the specified domain (${domain}). ${
dnsErrors[err.code] || err.message
}`;
err.response = `DNS Error: ${err.message}`;
err.temporary = true; // this might be a temporary issue with DNS
err.category = 'dns';
return reject(err);
}

if (!list || !list.length) {
// fallback to A
return dnsResolve(domain, (err, list) => {
if (err && err.code !== 'ENODATA' && err.code !== 'ENOTFOUND') {
err.message = 'DNS error when resolving MX server for ' + domain + ': ' + (dnsErrors[err.code] || err.message);
err.response = '450 ' + err.message;
err.message = `DNS error occurred while resolving the Mail Exchange (MX) server for the specified domain (${domain}). ${
dnsErrors[err.code] || err.message
}`;
err.response = `DNS Error: ${err.message}`;
err.temporary = true; // this might be a temporary issue with DNS
err.category = 'dns';
return reject(err);
}
Expand All @@ -77,17 +80,23 @@ async function resolveMX(delivery) {
// fallback to AAAA
return dnsResolve(domain, 'AAAA', (err, list) => {
if (err && err.code !== 'ENODATA' && err.code !== 'ENOTFOUND') {
err.message = 'DNS error when resolving MX server for ' + domain + ': ' + (dnsErrors[err.code] || err.message);
err.response = '450 ' + err.message;
err.message = `DNS error occurred while resolving the Mail Exchange (MX) server for the specified domain (${domain}). ${
dnsErrors[err.code] || err.message
}`;
err.response = `DNS Error: ${err.message}`;
err.temporary = true; // this might be a temporary issue with DNS
err.category = 'dns';
return reject(err);
}

if (!list || !list.length) {
// nothing found!
err = err || new Error('No MX server found');
err.message = 'DNS error when resolving MX server for ' + domain + ': ' + (dnsErrors[err.code] || err.message);
err.response = '550 ' + err.message;
err.message = `DNS error occurred while resolving the Mail Exchange (MX) server for the specified domain (${domain}). ${
dnsErrors[err.code] || err.message
}`;
err.response = `DNS Error: ${err.message}`;
err.code = err.code || 'ENOTFOUND';
err.category = 'dns';
return reject(err);
}
Expand Down
6 changes: 3 additions & 3 deletions lib/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,18 @@ module.exports.isInvalid = (delivery, ip) => {
if (dnsOptions.blockLocalAddresses) {
// check if exchange resolves to local IP range
if (['loopback', 'private'].includes(range)) {
return 'IP address in disallowed ' + range + ' range';
return `This IP address falls within the prohibited "${range}" address range, which is not valid for external communication.`;
}

// check if exchange resolves to local interface
if (module.exports.isLocal(ip)) {
return 'IP address in local interface';
return `The resolved IP address corresponds to a local interface.`;
}
}

// check if exchange resolves to invalid IP range
if (['unspecified', 'broadcast'].includes(range)) {
return 'IP address in disallowed ' + range + ' range';
return `The IP address is within the disallowed "${range}" address range, which is not permitted for direct communication.`;
}

return false;
Expand Down
Loading

0 comments on commit 324c021

Please sign in to comment.