Skip to content

Commit

Permalink
feat: added pool discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
titanism committed Sep 6, 2023
1 parent cfe3392 commit cbcef62
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 20 deletions.
5 changes: 2 additions & 3 deletions app/models/domains.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
const os = require('node:os');
const crypto = require('node:crypto');
const os = require('node:os');
const punycode = require('node:punycode');
const { promisify } = require('node:util');

const punycode = require('punycode/');

const Boom = require('@hapi/boom');
const RE2 = require('re2');
const _ = require('lodash');
Expand Down
3 changes: 1 addition & 2 deletions app/models/emails.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const punycode = require('node:punycode');
const { Buffer } = require('node:buffer');
const { isIP } = require('node:net');

const punycode = require('punycode/');

const Boom = require('@hapi/boom');
const SpamScanner = require('spamscanner');
const _ = require('lodash');
Expand Down
58 changes: 46 additions & 12 deletions helpers/get-transporter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { callbackify } = require('node:util');
const { isIP } = require('node:net');
const punycode = require('node:punycode');

const _ = require('lodash');
const isFQDN = require('is-fqdn');
Expand Down Expand Up @@ -56,6 +57,38 @@ async function getTransporter(connectionMap = new Map(), options = {}, err) {
return pool;
}

// otherwise lookup the MX records to determine if target != end resulting MX host
//
// NOTE: this is very rudimentary and will only attempt to re-use the pool for
// the first FQDN and lowest priority exchange found (which is acceptable and covers majority of use cases)
//
if (!isIP(target) && isFQDN(target)) {
try {
const list = await resolver.resolve(punycode.toASCII(target), 'MX');
if (list && list.length > 0) {
const sorted = list
.filter(
(o) =>
_.isObject(o) && isFQDN(o.exchange) && Number.isFinite(o.priority)
)
.sort((a, b) => a.priority - b.priority);
if (sorted.length > 0) {
const rootDomain = parseRootDomain(sorted[0].exchange);
if (
config.truthSources.has(parseRootDomain(rootDomain)) &&
connectionMap.has(`${rootDomain}:${port}`)
) {
const pool = connectionMap.get(`${rootDomain}:${port}`);
logger.info(`pool discovered: ${key} (${rootDomain}:${port})`);
return pool;
}
}
}
} catch (err) {
logger.warn(err);
}
}

// <https://github.com/zone-eu/mx-connect#configuration-options>
const mx = await asyncMxConnect({
ignoreMXHosts,
Expand Down Expand Up @@ -84,14 +117,6 @@ async function getTransporter(connectionMap = new Map(), options = {}, err) {
}
});

if (!err) {
mx.socket.once('close', () => {
logger.info(`pool closed: ${key}`);
// remove the socket from the available pool
connectionMap.delete(key);
});
}

//
// if the SMTP response was from trusted root host and it was rejected for spam
// then denylist the sender (probably a low-reputation domain name spammer)
Expand All @@ -107,6 +132,17 @@ async function getTransporter(connectionMap = new Map(), options = {}, err) {
)
truthSource = parseRootDomain(mx.hostname);

const isPooling = typeof err === 'undefined' && truthSource;

if (isPooling) {
mx.socket.once('close', () => {
logger.info(`pool closed: ${truthSource}:${port}`);
// remove the socket from the available pool
if (connectionMap.has(`${truthSource}:${port}`))
connectionMap.delete(`${truthSource}:${port}`);
});
}

const requireTLS = Boolean(
mx.policyMatch && mx.policyMatch.mode === 'enforce'
);
Expand Down Expand Up @@ -146,8 +182,6 @@ async function getTransporter(connectionMap = new Map(), options = {}, err) {

const opportunisticTLS = Boolean(!requireTLS && !ignoreTLS);

const isPooling = typeof err === 'undefined' && truthSource;

// TODO: may need to pass custom `getSocket` option if `isPooling`

const transporter = nodemailer.createTransport({
Expand Down Expand Up @@ -177,8 +211,8 @@ async function getTransporter(connectionMap = new Map(), options = {}, err) {
};

if (!err && isPooling) {
connectionMap.set(key, pool);
logger.info(`pool created: ${key}`);
connectionMap.set(`${truthSource}:${port}`, pool);
logger.info(`pool created: ${truthSource}:${port}`);
}

return pool;
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@
"pretty-ms": "7",
"preview-email": "3.0.19",
"pug": "3.0.2",
"punycode": "2.3.0",
"qrcode": "1.5.3",
"qs": "6.11.2",
"re2": "1.17.7",
Expand Down
3 changes: 1 addition & 2 deletions smtp-server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const fs = require('node:fs');

const punycode = require('punycode/');
const punycode = require('node:punycode');

const RateLimiter = require('async-ratelimiter');
const _ = require('lodash');
Expand Down

0 comments on commit cbcef62

Please sign in to comment.