0

我目前正在开发一个 C# 项目,我正在制作自己的 SMTP 服务器。它的工作量更少,但我正在努力让它工作,我可以向不同域上的多个收件人发送电子邮件。

我最初是这样做的,所以我会创建一个MailMesssage对象并运行以下命令来添加每个收件人

MailMessage message = new MailMessage();
message.To.add("someone@domain1.com");
message.To.add("someone@domain2.com");

如果不同的域通过谷歌应用服务器,我会得到 MX 记录,即ALT2.ASPMX.L.GOOGLE.COM. 当发送邮件时,虽然在上面添加了收件人,但谷歌会发回一个错误,因为他们不允许通过一个 SMTP 会话进行跨域发送。

因此,我对其进行了重新设计,以便为每个收件人发送单独的电子邮件,我获得每个域的 MX 记录,因此我最终也得到了不同的 SMTP 会话。所以message.To.add我收到的每个收件人只有一个。我想要做的是添加一个标题,这样它仍然显示电子邮件收件人仍然会去someone@domain1.comsomeone@domain2.com.

因此,就 MailMessage 组件而言,只有一个收件人,但标头显示多个收件人,因此当收到的邮件查看其客户端中的电子邮件时,它会显示该电子邮件到达的所有收件人。

以下是我必须发送电子邮件的代码。

MXLookup mxLookup = new MXLookup();
                    List<string> recipients = addRecipientsToEmail(message.emailRecipients);
                    foreach (string recipient in recipients)
                    {
                        string domain = Classes.CommonTasks.getDomainFromEmail(recipient);
                        string[] mxRecords = mxLookup.getMXRecords(Classes.CommonTasks.getDomainFromEmail(domain));
                        if (mxRecords != null)
                        {
                            MailMessage composedMail = new MailMessage();
                            composedMail.From = new MailAddress(message.EmailFromAddress);
                            composedMail.To.Add(recipient);
                            composedMail.Subject = message.subject;
                            composedMail.Body = message.EmailBody;
                            composedMail.Headers.Add(getHeaders(recipients));

                            if (message.contentType.ToString().Contains("text/html"))
                            {
                                composedMail.IsBodyHtml = true;
                            }

                            SmtpClient smtp = new SmtpClient(mxRecords[0]);
                            smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
                            smtp.Port = 25;
                            if (Configuration.emailConfig.useSmtpMaxIdleTime)
                            {
                                smtp.ServicePoint.MaxIdleTime = 1;
                            }
                            library.logging(methodInfo, string.Format("Sending email via MX Record: {0}", mxRecords[0]));
                            smtp.Send(composedMail);
                            updateEmailStatus(message.emailID, EmailStatus.Sent);
                            library.logging(methodInfo, string.Format("Successfully sent email ID: {0}", message.emailID));

                        }

                        else
                        {
                            string error = string.Format("No MX Record found for domain: {0}", domain);
                            library.logging(methodInfo, error);
                            library.setAlarm(error, CommonTasks.AlarmStatus.Warning, methodInfo);
                        }

下面是我的 getHeaders 函数。

private NameValueCollection getHeaders(List<string> emailRecipients)
{
    string headers = "";
    NameValueCollection headersArray = new NameValueCollection();
    foreach (string recipient in emailRecipients)
    {
        headers += string.Format("{0}, ", recipient);
    }
    headersArray.Add("To", headers);
    return headersArray;
}

感谢您的任何帮助,您可以提供。

4

1 回答 1

1

Boardy,首先,让我说我理解您为什么要使用自己的 SMTP 服务器而不是中继。我们正在做同样的事情,因为我们需要实时处理电子邮件故障,而不必依赖死信或 NDR。这只有在您控制传输通道时才实用。

您在这里遇到的问题是无法将 RCPT TO 命令和“To”标头分开。如果您查看 Exchange 的wireshark 日志(即)发送如上所述的电子邮件,它将有两个端口 25(大概)会话,一次到 domain1,然后再到 domain2。在第一个会话中,RCPT TO将是someone1@domain1.com并且 To 标头将是To:someone1@domain1.com, somenone2@domain2.com。对于第二个会话,To 标头将相同,但RCPT TO将是someone2@domain2.com. 在 .net 类中,To 集合实际上是 RCPT TO,似乎我们无法访问 To 标头。当然,如果您尝试这样做并实际添加了两个收件人,domain1 将为某人@domain2.com 提供中继错误(反之亦然)。理想情况下,我们可以将 RCPT 集合设置为收件人,而 To(和 CC)集合可以是一个标头包装器,它可以根据 RCPT 信息默认设置,或者我们可以覆盖。就目前而言,没有办法使用 .net 类来真正实现您自己的邮件服务器。

我已经为此苦苦挣扎了几年。它最近刚刚成为一个问题,因为我们开始使用 CC 和多个 Tos,所以我正在寻找解决方案。目前我正在考虑使用自定义套接字实现或使用 Rebex。他们似乎有我需要的东西,但我不能说我已经按照我的意愿让它工作了。当然,如果有一些很棒的反射技巧(就像我们对 FQDN 所做的那样)来使这项工作在本地进行,那将是很高兴知道的。简而言之,您的问题的答案是 AFAIK,不能那样做。我会发回我对 Rebex 的任何经验。

于 2013-10-30T00:59:52.560 回答