mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-03-01 02:15:15 +08:00
v1.28.2
This commit is contained in:
parent
4678768026
commit
8f31640424
6 changed files with 61 additions and 5 deletions
|
@ -8,3 +8,8 @@ gfs="mail"
|
|||
# see [dbs].sender option for choosing correct database to use for ZoneMTA queues
|
||||
# by default the main wildduck database is used
|
||||
collection="zone-queue"
|
||||
|
||||
# Hashing secret for loop detection
|
||||
# Must be shared with haraka-plugin-wildduck
|
||||
# If not set then looping is not tracked
|
||||
loopSecret=""
|
|
@ -25,7 +25,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler) => {
|
|||
db,
|
||||
zone: config.sender.zone,
|
||||
collection: config.sender.collection,
|
||||
gfs: config.sender.gfs
|
||||
gfs: config.sender.gfs,
|
||||
loopSecret: config.sender.loopSecret
|
||||
});
|
||||
|
||||
const putMessage = util.promisify(messageHandler.put.bind(messageHandler));
|
||||
|
|
|
@ -34,7 +34,8 @@ module.exports = (db, server, messageHandler, userHandler) => {
|
|||
db,
|
||||
zone: config.sender.zone,
|
||||
collection: config.sender.collection,
|
||||
gfs: config.sender.gfs
|
||||
gfs: config.sender.gfs,
|
||||
loopSecret: config.sender.loopSecret
|
||||
});
|
||||
|
||||
function submitMessage(options, callback) {
|
||||
|
|
|
@ -35,7 +35,8 @@ class FilterHandler {
|
|||
db: this.db,
|
||||
zone: options.sender.zone,
|
||||
collection: options.sender.collection,
|
||||
gfs: options.sender.gfs
|
||||
gfs: options.sender.gfs,
|
||||
loopSecret: options.sender.loopSecret
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ module.exports = (options, callback) => {
|
|||
db,
|
||||
zone: config.sender.zone,
|
||||
collection: config.sender.collection,
|
||||
gfs: config.sender.gfs
|
||||
gfs: config.sender.gfs,
|
||||
loopSecret: config.sender.loopSecret
|
||||
});
|
||||
|
||||
return maildropper.push(options, callback);
|
||||
|
|
|
@ -10,6 +10,7 @@ const os = require('os');
|
|||
const hostname = os.hostname().toLowerCase();
|
||||
const addressparser = require('nodemailer/lib/addressparser');
|
||||
const punycode = require('punycode');
|
||||
const crypto = require('crypto');
|
||||
const tools = require('./tools');
|
||||
|
||||
class Maildropper {
|
||||
|
@ -27,6 +28,42 @@ class Maildropper {
|
|||
});
|
||||
}
|
||||
|
||||
checkLoop(envelope, deliveries) {
|
||||
if (envelope.reason !== 'forward' || !this.options.loopSecret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const loopKey = 'X-WildDuck-Seen';
|
||||
const algo = 'sha256';
|
||||
const secret = this.options.loopSecret;
|
||||
|
||||
const targetStr = JSON.stringify(deliveries);
|
||||
|
||||
const loopFields = envelope.headers.getDecoded(loopKey);
|
||||
|
||||
// check existing loop headers (max 100 to avoid checking too many hashes)
|
||||
for (let i = 0, len = Math.min(loopFields.length, 100); i < len; i++) {
|
||||
let field = (loopFields[i].value || '').toLowerCase().trim();
|
||||
let salt = field.substr(0, 12);
|
||||
let hash = field.substr(12);
|
||||
let hmac = crypto.createHmac(algo, secret);
|
||||
hmac.update(salt);
|
||||
hmac.update(targetStr);
|
||||
let result = hmac.digest('hex');
|
||||
if (result.toLowerCase() === hash) {
|
||||
// Loop detected!
|
||||
envelope.looped = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const salt = crypto.randomBytes(6).toString('hex').toLowerCase();
|
||||
const loopHeader = salt + crypto.createHmac(algo, secret).update(salt).update(targetStr).digest('hex').toLocaleLowerCase();
|
||||
envelope.headers.add(loopKey, loopHeader);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
push(options, callback) {
|
||||
let id = options.id || seqIndex.get();
|
||||
let seq = 0;
|
||||
|
@ -140,6 +177,13 @@ class Maildropper {
|
|||
return callback(err);
|
||||
}
|
||||
|
||||
if (this.checkLoop(envelope, deliveries)) {
|
||||
// looped message
|
||||
let err = new Error('Message loop detected');
|
||||
err.code = 'ELOOP';
|
||||
return this.removeMessage(id, () => callback(err));
|
||||
}
|
||||
|
||||
envelope.headers = envelope.headers.getList();
|
||||
this.setMeta(id, envelope, err => {
|
||||
if (err) {
|
||||
|
@ -234,7 +278,10 @@ class Maildropper {
|
|||
}
|
||||
|
||||
parseAddressList(headers, key, withNames) {
|
||||
return this.parseAddressses(headers.getDecoded(key).map(header => header.value), withNames);
|
||||
return this.parseAddressses(
|
||||
headers.getDecoded(key).map(header => header.value),
|
||||
withNames
|
||||
);
|
||||
}
|
||||
|
||||
parseAddressses(headerList, withNames) {
|
||||
|
|
Loading…
Reference in a new issue