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



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

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

我决定使用Mailgun catch_all()forward("https://exmaple.com/incoming-email?key=SECRET_KEY"),并为此编写了一些节点.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;

如您所见,该代码能够将任何传入的电子邮件转发到本地主机SMTP服务器。使用关于的代码,我可以使用 user@example.com 接收电子邮件。收到的电子邮件将包含正确的发件人、收件人、抄送、主题和正文,甚至附件。

然而,一天后,我被迫取消订阅 Arch Linux 邮件列表,一个人通过电子邮件告诉我:

您的邮件服务器似乎配置错误,并且至少发送了 它从我们的邮件列表中收到的一些邮件返回到列表。它还 删除过程中的一些标头,以帮助邮递员检测此类标头 行为。由于缺少这些标头,邮递员将发送邮件 再次向所有人展示,导致一个永无止境的循环。

在我呼救后,我也收到了一些电子邮件,表明我在此期间无法收到来自邮件列表的任何电子邮件。

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

我发现当电子邮件是密件抄送给您时,邮件将如下所示:

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

这样在使用问题中的代码时,邮件将由节点.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;

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

请参阅:在 SMTP 中,RCPT TO: 和 TO: 必须匹配吗?

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

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

最新更新