使用SmtpClient发送邮件时出现“服务器违反协议”的故障排除

8

我想使用SmtpClient类发送邮件消息。

这是我使用的代码:

SmtpClient smtpClient = new SmtpClient("Host",25);
NetworkCredential basicCredential =
new NetworkCredential("UserName", "Password");
MailMessage message = new MailMessage();
MailAddress fromAddress = new MailAddress("me@domain.com");
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = basicCredential;
message.From = fromAddress;
message.Subject = "test send";
message.IsBodyHtml = true;
message.Body = "<h1>hello</h1>";
message.To.Add("mail@domain.com");
smtpClient.Send(message);

但它总是抛出一个异常:

服务器违反了协议,服务器的响应是:UGFzc3dvcmQ6

我找不到原因。如果有人遇到过这样的情况,请告诉我该怎么做。

8个回答

8
我有同样的问题,对于我的情况,是因为设置了用户@域名而不是仅仅用户。
旧代码。
new NetworkCredential("UserName@domain.com", "Password");

新代码
new NetworkCredential("UserName", "Password");

5

在我看来,这似乎是 SmtpClient 的身份验证出了问题。

有些身份验证机制是“客户端:请求使用用户名和密码进行身份验证,服务器:成功/失败”,而另一些则是“客户端:请求使用用户名进行身份验证,服务器:请求密码,客户端:回复密码,服务器:成功/失败”。

看起来 SmtpClient 期望前者,而你的服务器期望后者。

正如 dave wenta 建议的那样,会话日志将告诉您 SmtpClient 尝试使用哪种身份验证机制,但它也会说出服务器支持哪些身份验证机制。

通常情况下,服务器会提供多个身份验证选项,客户端选择要使用的选项。从那里开始的行为应该由所选择的协议确定。我希望 SmtpClient 类已经为您处理了这个问题,但恐怕我从未使用过这个特定的类。

还要记住 - 如果您要在此处发布日志,请在记录会话之前更改为一次性密码,因为base64编码的纯文本密码可以轻松地转换回人类可读的纯文本密码。


谢谢回复,我相信你所说的服务器正在期望Base64身份验证信息。 - Mina Wissa
这个也不起作用,我已经检查了日志,但它没有显示任何服务器响应。你能帮忙吗? - Naman Upadhyay

4

UGFzc3dvcmQ6是"Password:"的base64编码(不包含引号),这意味着密码可能错误或没有进行编码发送。请尝试对密码进行base64编码:

string password = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("Password));

抱歉,伙计们。错误已经消失了,但是出现了一个新的异常,即发送邮件失败。不知道该怎么办。 - Mina Wissa

2

启用System.Net.Mail的日志记录功能,然后查看日志文件。这将向您展示SMTP层期间发生的确切情况。

以下链接提供了更多信息:

http://systemnetmail.com/faq/4.10.aspx


五年后,这对我帮助很大——我发现我在进行连接窃取。 - Dreamcasting
@dave,答案中提到的链接已经失效。 - Jenish Rabadiya

1

我遇到了同样的问题。按照以下步骤解决: 1)通过Telnet或putty或其他终端连接到SMTP服务器,找出服务器提供的SMTP身份验证方式:

telnet xxx.yyy.zzz.aaa 587 

(xxx.yyy.zzz.aaa = SMTP服务器的IP地址,587 = 端口号)

< 服务器回复 "220 协议 + 版本 + 时间"

ehlo testing

服务器显示功能列表,例如:
250-AUTH NTLM CRAM-MD5 LOGIN

SMTP客户端会尝试首选最安全的协议。在我的情况下:
1. System.Net.Mail.SmtpNegotiateAuthenticationModule
2. System.Net.Mail.SmtpNtlmAuthenticationModule
3. System.Net.Mail.SmtpDigestAuthenticationModule
4. System.Net.Mail.SmtpLoginAuthenticationModule

看起来SMTP客户端尝试使用NTLM,而服务器则尝试运行LOGIN。

通过一个黑科技(参见https://blogs.msdn.microsoft.com/knom/2008/04/16/hacking-system-net-mail-smtpclient/),可以关闭所有协议,除了服务器假定的那个协议(在这种情况下为LOGIN):

FieldInfo transport = smtpClient.GetType().GetField("transport", BindingFlags.NonPublic | BindingFlags.Instance);

FieldInfo authModules = transport.GetValue(smtpClient).GetType().GetField("authenticationModules",BindingFlags.NonPublic | BindingFlags.Instance);

Array modulesArray = authModules.GetValue(transport.GetValue(smtpClient)) as Array;
foreach (var module in modulesArray)
{
    Console.WriteLine(module.ToString());
}
// System.Net.Mail.SmtpNegotiateAuthenticationModule
// System.Net.Mail.SmtpNtlmAuthenticationModule
// System.Net.Mail.SmtpDigestAuthenticationModule
// System.Net.Mail.SmtpLoginAuthenticationModule

// overwrite the protocols that you don't want
modulesArray.SetValue(modulesArray.GetValue(3), 0);
modulesArray.SetValue(modulesArray.GetValue(3), 1);
modulesArray.SetValue(modulesArray.GetValue(3), 2);

以上代码存在一些问题(至少在 .netcore 2.1 中),字段名前需要加下划线。因此,上述代码需要添加 "_transport" 和 "_authenticationModules" 字段。除此之外,上述代码基本可用。 - Peter Avram

1

请注意,base64只是一种编码方式。虽然base64密码不明显,但它也没有被加密。将“TXlQYSRzdzByZA==”输入http://www.opinionatedgeek.com/dotnet/tools/base64decode/,就会弹出“密码”。 - Mark Booth

0

对于其他在谷歌上遇到这个问题的人..我通过以“用户名”格式而不是“域\用户名”的方式提供我的用户名来解决它。

$Credential = Get-Credential

Send-MailMessage -SmtpServer exchange.contoso.com -Credential $Credential -From 'user@contoso.com' -To 'recipient@domain.com' -Subject 'Test email' -Port 587 -UseSsl

0

这也可能发生在你没有提供密码时。在我的情况下,我正在使用从 web.config smtp 块中获取的凭据,并在我的部署服务器(使用章鱼部署)上忘记了填充密码属性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接