使用HTTP POST路由将Mailgun的传入电子邮件转发到自己的SMTP服务器时出现问题

时间:2016-01-14 17:49:14

标签: node.js email mailgun nodemailer

最近我不得不使用CloudFlare隐藏我的IP地址,但是我的MX记录指向我自己的域名,我不能。

我知道Mailgun可以将所有电子邮件转发到user@example.com到Gmail,但我不想这样做。我希望这封电子邮件可以转发到example.com自己的SMTP服务器上。

我决定使用Mailgun catch_all()forward("https://exmaple.com/incoming-email?key=SECRET_KEY"),并为此编写了一些node.js代码。

var nodemailer = require('nodemailer');
var express = require('express');
var multer = require('multer');
var router = express.Router();

var KEY = 'SECRET_KEY';
var ADDITIONAL_HEADERS = ['timestamp', 'token', 'signature', 'mime-version'];
var transporter = nodemailer.createTransport('smtp://127.0.0.1');

var storage = multer.memoryStorage();
var upload = multer({
  storage: storage
});

router.post('/', upload.any(), function (req, res, next) {
  if (!req.query.key || req.query.key !== KEY) {
    res.sendStatus(403);
    return;
  }
  console.log('Incoming mail: ', req.body.From);
  req.body.to = req.body.To;
  req.body.cc = req.body.Cc;
  req.body.from = req.body.From;
  req.body.date = req.body.Date;
  req.body.references = req.body.References;
  req.body.replyTo = req.body['Reply-To'];
  req.body.inReplyTo = req.body['In-Reply-To'];
  req.body.messageId = req.body['Message-Id'];
  req.body.html = req.body['body-html'];
  req.body.text = req.body['body-plain'];
  req.body.headers = {};
  for (var key in req.body) {
    if (key.toLowerCase().indexOf('X-') !== -1 ||
      ADDITIONAL_HEADERS.indexOf(key.toLowerCase()) !== -1) {
      req.body.headers[key] = req.body[key];
    }
  }
  if (req.files) {
    req.body.attachments = req.files;
    req.body.attachments.forEach(function (attachment) {
      attachment.filename = attachment.originalname;
      attachment.contentType = attachment.mimetype;
      attachment.content = attachment.buffer;
    });
  }
  transporter.sendMail(req.body, function (err, info) {
    if (err) {
      console.log(err);
      res.status(400).send(err);
      return;
    }
    console.log(info);
    res.send(info);
  });
});

module.exports = router;

如您所见,代码可以将任何传入的电子邮件转发到localhost SMTP服务器。使用代码,我可以使用user@example.com接收电子邮件。收到的电子邮件将具有正确的from,to,cc,subject和body甚至附件。

然而,一天后,我被迫取消订阅Arch Linux邮件列表,并且有人通过电子邮件发送给我:

  

您的邮件服务器似乎配置错​​误并至少发送   它从邮件列表收到的一些邮件返回列表。它也是   删除过程中的一些标题,帮助mailman检测这样的标题   行为。由于缺少这些标题,mailman将发送邮件   再次向所有人发出,导致永无止境的循环。

在我求助之后,我还收到了一些电子邮件,表明我在此期间无法收到来自邮件列表的任何电子邮件。

为什么会出错?什么是"一些标题"在转发/包装过程中删除的电子邮件中?是Mailgun的问题还是我自己的问题?如果这是我的问题,可以在上面的代码中添加什么?感谢。

2 个答案:

答案 0 :(得分:1)

我发现当电子邮件是您的密件抄送时,邮件将显示为:

  • 收件人:announce@example-mailing-list.com
  • 密送:you@example.com

因此,当使用问题中的代码时,邮件将由node.js代码处理,然后邮件将使用smtp://127.0.0.1发送回announce@example-mailing-list.com,从而导致无限循环。

我已更新我的代码,以便从收件人列表中查找我的域的所有电子邮件地址,并将其设置为To字段,并且无限循环不再出现。

例如,收件人列表将显示为:announce@example-mailing-list.com, you@example.com,因此我发现所有以@example.com结尾的电子邮件地址并将其设为To字段。

警告:但有些信息会丢失,就像我再也看不到电子邮件的原始To字段了。但是,您可以将原始To字段附加到任何字段,例如subject

var nodemailer = require('nodemailer');
var express = require('express');
var multer = require('multer');
var router = express.Router();

var MY_DOMAIN = 'example.com';
var KEY = 'SECRET_KEY;
var ADDITIONAL_HEADERS = ['timestamp', 'token', 'signature', 'mime-version',
  'return-path', 'dkim-signature'
];
var transporter = nodemailer.createTransport('smtp://127.0.0.1');

var storage = multer.memoryStorage();
var upload = multer({
  storage: storage
});

router.post('/', upload.any(), function (req, res, next) {
  if (!req.query.key || req.query.key !== KEY) {
    res.sendStatus(403);
    return;
  }
  console.log('Incoming mail: ', req.body.From);
  req.body.to = req.body.recipient.split(', ').filter(email => email.indexOf('@' + MY_DOMAIN) !== -1);
  req.body.from = req.body.From;
  req.body.date = req.body.Date;
  req.body.references = req.body.References;
  req.body.replyTo = req.body['Reply-To'];
  req.body.inReplyTo = req.body['In-Reply-To'];
  req.body.messageId = req.body['Message-Id'];
  req.body.html = req.body['body-html'];
  req.body.text = req.body['body-plain'];
  req.body.headers = {};
  for (var key in req.body) {
    if (key.toLowerCase().indexOf('x-') !== -1 ||
      ADDITIONAL_HEADERS.indexOf(key.toLowerCase()) !== -1) {
      req.body.headers[key] = req.body[key];
    }
  }
  console.log('Params: ' + JSON.stringify(req.body));
  if (req.files) {
    req.body.attachments = req.files;
    req.body.attachments.forEach(function (attachment) {
      attachment.filename = attachment.originalname;
      attachment.contentType = attachment.mimetype;
      attachment.content = attachment.buffer;
    });
  }
  transporter.sendMail(req.body, function (err, info) {
    if (err) {
      console.log(err);
      res.status(400).send(err);
      return;
    }
    console.log(info);
    res.send(info);
  });
});

module.exports = router;

答案 1 :(得分:0)

电子邮件不仅具有To标头,还具有SMTP RCPT TO值。此值可能与电子邮件中包含的To标头不同。

请参阅:In SMTP, must the RCPT TO: and TO: match?

您的代码似乎使用邮件内容,但似乎没有单独处理传输收件人(SMTP RCPT TO)。我不知道mailgun是否提供了这个值。

只需将邮件提交到您的smtp服务器,沿途的某些库可能会从To标头中提取收件人,但是对于邮件列表,这通常是不正确的。