Skip to content

Commit

Permalink
feat: added custom port forwarding support (closes #163), bump deps
Browse files Browse the repository at this point in the history
  • Loading branch information
niftylettuce committed May 6, 2020
1 parent 30075e9 commit 0c2f6a4
Show file tree
Hide file tree
Showing 6 changed files with 587 additions and 1,070 deletions.
10 changes: 5 additions & 5 deletions .env.defaults
Expand Up @@ -19,14 +19,14 @@ DNSBL_REMOVALS=https://www.spamhaus.org/lookup/
SPAM_SCORE_THRESHOLD=5
MAX_FORWARDED_ADDRESSES=10
TXT_RECORD_PREFIX=forward-email
BLACKLIST=biz-catalogs.com,tiproulette.com,mail.tiproulette.com,mail.starebuck.store,starebuck.store,edatabases.email,mailer.edatabases.email,reactivatoinswwwhttps.org,acemsrvc.com,delta-solar.com,committeetoprotect.org,ionexchange.co.in,grupojutse.com,skywide.co.ke,ukoverseasbusinessvisa.com,thinali.com,yklivuwk.mail.padsdel.com,mail.padsdel.com,padsdel.com,aviatorsolutiont.org,sings.tech,sealking.com.tw,talenthunter.sg,ulterta.rest,mta.bgrindonesia.co.id,bgrindonesia.co.id
BLACKLIST=biz-catalogs.com,tiproulette.com,mail.tiproulette.com,mail.starebuck.store,starebuck.store,edatabases.email,mailer.edatabases.email,reactivatoinswwwhttps.org,acemsrvc.com,delta-solar.com,committeetoprotect.org,ionexchange.co.in,grupojutse.com,skywide.co.ke,ukoverseasbusinessvisa.com,thinali.com,yklivuwk.mail.padsdel.com,mail.padsdel.com,padsdel.com,aviatorsolutiont.org,sings.tech,sealking.com.tw,talenthunter.sg,ulterta.rest,mta.bgrindonesia.co.id,bgrindonesia.co.id,phenix-anima.com
CERTBOT_WELL_KNOWN_NAME=
CERTBOT_WELL_KNOWN_CONTENTS=
RATELIMIT_DURATION=3600000
RATELIMIT_MAX=300
SMTP_RATELIMIT_DURATION=3600000
SMTP_RATELIMIT_MAX=300
VANITY_DOMAINS=secret.fyi,hash.fyi,mailsire.com,hideaddress.net
LOOKUP_ENDPOINT=https://forwardemail.net:4000/v1/lookup
LOOKUP_SECRETS=secret,
API_ENDPOINT=https://forwardemail.net:4000
API_SECRETS=secret,
SRS_SECRET=
SRS_DOMAIN=forwardemail.net
DHPARAM_KEY_PATH=
8 changes: 4 additions & 4 deletions .env.schema
Expand Up @@ -22,11 +22,11 @@ DNSBL_REMOVALS=
BLACKLIST=
CERTBOT_WELL_KNOWN_NAME=
CERTBOT_WELL_KNOWN_CONTENTS=
RATELIMIT_DURATION=
RATELIMIT_MAX=
SMTP_RATELIMIT_DURATION=
SMTP_RATELIMIT_MAX=
VANITY_DOMAINS=
LOOKUP_ENDPOINT=
LOOKUP_SECRETS=
API_ENDPOINT=
API_SECRETS=
SRS_SECRET=
SRS_DOMAIN=
DHPARAM_KEY_PATH=
1 change: 0 additions & 1 deletion .travis.yml
@@ -1,6 +1,5 @@
language: node_js
node_js:
- '10'
- '12'
- 'lts/*'
- 'node'
Expand Down
65 changes: 46 additions & 19 deletions index.js
Expand Up @@ -118,7 +118,8 @@ const transporterConfig = {
logger,
direct: true,
opportunisticTLS: true,
port: 25,
// this can be overridden now
// port: 25,
tls: {
rejectUnauthorized: env.NODE_ENV !== 'test'
},
Expand Down Expand Up @@ -163,15 +164,6 @@ class ForwardEmail {
removals: env.DNSBL_REMOVALS,
...config.dnsbl
},
rateLimit: {
duration: env.RATELIMIT_DURATION
? parseInt(env.RATELIMIT_DURATION, 10)
: 60000,
max: env.RATELIMIT_MAX ? parseInt(env.RATELIMIT_MAX, 10) : 100,
prefix: env.RATELIMIT_PREFIX
? env.RATELIMIT_PREFIX
: `limit_${env.NODE_ENV.toLowerCase()}`
},
exchanges: env.SMTP_EXCHANGE_DOMAINS,
dkim: {
domainName: env.DKIM_DOMAIN_NAME,
Expand Down Expand Up @@ -229,8 +221,8 @@ class ForwardEmail {
website: env.WEBSITE_URL,
recordPrefix: env.TXT_RECORD_PREFIX,
whitelistedDisposableDomains: env.VANITY_DOMAINS,
lookupEndpoint: env.LOOKUP_ENDPOINT,
lookupSecrets: env.LOOKUP_SECRETS,
apiEndpoint: env.API_ENDPOINT,
apiSecrets: env.API_SECRETS,
srs: {
separator: '=',
secret: env.SRS_SECRET,
Expand Down Expand Up @@ -356,7 +348,7 @@ class ForwardEmail {

processRecipient(options) {
const { recipient, name, from, raw } = options;
const { address, addresses } = recipient;
const { address, addresses, port } = recipient;
return Promise.all(
addresses.map(({ to, host }) => {
return this.processAddress(address, {
Expand All @@ -366,7 +358,8 @@ class ForwardEmail {
from,
to
},
raw
raw,
port
});
})
);
Expand Down Expand Up @@ -397,13 +390,15 @@ class ForwardEmail {
// TODO: eventually we can combine multiple recipients
// that have the same MX records in the same envelope `to`
sendEmail(options) {
const { host, name, envelope, raw } = options;
const { host, name, envelope, raw, port } = options;
const transporter = nodemailer.createTransport({
...transporterConfig,
...this.config.ssl,
port: parseInt(port, 10),
host,
name
});
logger.debug('sendEmail', { envelope, port, host, name, raw });
return transporter.sendMail({ envelope, raw });
}

Expand Down Expand Up @@ -1011,8 +1006,35 @@ class ForwardEmail {
if (addresses === false)
return { address: to.address, addresses: [], ignored: true };

// lookup the port (e.g. if `forward-email-port=` or custom set on the domain)
let port = '25';
try {
const domain = this.parseDomain(to.address, false);
const { body } = await got.get(
`${this.config.apiEndpoint}/v1/port?domain=${domain}`,
{
responseType: 'json',
username: this.config.apiSecrets[0]
}
);
// body is an Object with `port` Number (a valid port number, defaults to 25)
if (
_.isObject(body) &&
isSANB(body.port) &&
validator.isPort(body.port) &&
body.port !== '25'
) {
port = body.port;
logger.debug(`Custom port for ${to.address} detected`, {
port
});
}
} catch (err) {
logger.error(err);
}

// if we already rewrote headers no need to continue
if (rewritten) return { address: to.address, addresses };
if (rewritten) return { address: to.address, addresses, port };

// the same address that gets forwarded TO using our service
// (we can assume that other mail providers do the same)
Expand Down Expand Up @@ -1043,7 +1065,7 @@ class ForwardEmail {
}
}

return { address: to.address, addresses };
return { address: to.address, addresses, port };
} catch (err) {
logger.error(err);
bounces.push({
Expand Down Expand Up @@ -1135,12 +1157,16 @@ class ForwardEmail {
// (eventually we call `dkim.sign(raw)` and pass it to nodemailer's `raw` option)
const raw = this.dkim.sign(Buffer.concat([headers.build(), ...chunks]));

//
// 11) set from address using SRS
//
const from = this.srs.forward(
session.envelope.mailFrom.address,
this.config.srsDomain
);

logger.debug('recipients', { recipients });

//
// 12) send email
//
Expand Down Expand Up @@ -1452,10 +1478,10 @@ class ForwardEmail {
// if there was a verification record then perform lookup
try {
const { body } = await got.get(
`${this.config.lookupEndpoint}?verification_record=${verifications[0]}`,
`${this.config.apiEndpoint}/v1/lookup?verification_record=${verifications[0]}`,
{
responseType: 'json',
username: this.config.lookupSecrets[0]
username: this.config.apiSecrets[0]
}
);
// body is an Array of records that are formatted like TXT records
Expand Down Expand Up @@ -1622,6 +1648,7 @@ class ForwardEmail {
// test:h.com
// ...

// TODO: lookup here to determine max forwarded addresses on the domain
// if max number of forwarding addresses exceeded
if (forwardingAddresses.length > this.config.maxForwardedAddresses)
throw new CustomError(
Expand Down
21 changes: 11 additions & 10 deletions package.json
Expand Up @@ -7,8 +7,9 @@
"serial": true,
"failFast": true,
"verbose": true,
"helpers": [
"test/helpers/**/*"
"files": [
"test/**/*",
"!test/helpers"
]
},
"bugs": {
Expand All @@ -28,18 +29,18 @@
"@ladjs/graceful": "^1.0.0",
"@ladjs/proxy": "^1.0.3",
"@ladjs/redis": "^1.0.3",
"@ladjs/shared-config": "^1.0.6",
"@ladjs/shared-config": "^2.0.0",
"array-join-conjunction": "^1.0.0",
"boolean": "^3.0.1",
"bytes": "^3.1.0",
"cabin": "^6.1.1",
"common-tags": "^1.8.0",
"disposable-email-domains": "^1.0.54",
"dmarc-parse": "^1.1.0",
"dmarc-parse": "^1.2.1",
"dnsbl": "^3.2.0",
"get-fqdn": "^0.0.4",
"get-port": "^5.1.1",
"got": "^11.0.2",
"got": "^11.1.1",
"ip": "^1.1.5",
"is-string-and-not-blank": "^0.0.2",
"lodash": "^4.17.15",
Expand All @@ -60,21 +61,21 @@
"devDependencies": {
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"ava": "2.x",
"ava": "^3.8.1",
"codecov": "^3.6.5",
"cross-env": "6.x",
"cross-env": "^7.0.2",
"eslint": "^6.8.0",
"eslint-config-xo-lass": "^1.0.3",
"fixpack": "^3.0.6",
"husky": "3.x",
"husky": "^4.2.5",
"ioredis": "^4.16.3",
"is-ci": "^2.0.0",
"lint-staged": "^10.1.7",
"lint-staged": "^10.2.2",
"nyc": "^15.0.1",
"remark-cli": "^8.0.0",
"remark-preset-github": "^1.0.0",
"shelljs": "^0.8.4",
"uuid": "^7.0.3",
"uuid": "^8.0.0",
"xo": "0.25"
},
"engines": {
Expand Down

0 comments on commit 0c2f6a4

Please sign in to comment.