为什么 SMTP 客户端会发送 RSET(重置)命令?

Ant*_*gan 9 smtp postfix ssmtp

我试图了解 Postfix 究竟是如何处理电子邮件的——以及 SMTP 邮件交易的一些更详细的细节。我的短期目标是调试一个专有的(二进制的、闭源的)SMTP 客户端,但我首先想我会检查成功的 SMTP 事务中会发生什么。

我计划在我们的 LAN 防火墙阻止传出 SMTP(端口 25),因此我将 Postfix 配置为内部邮件服务器,以接受来自(原始)本地客户端软件的邮件,该软件只能通过未经身份验证的 SMTP(通过端口 25)发送电子邮件。

smtpd通过在Postfix 日志疑难解答-vmaster.cf描述的附加详细标志来打开 Postfix进程的调试。然后,我使用 Cygwin Mutt 和sSMTP( 的最小实现)从我自己的工作站发送了一封电子邮件。sendmail

Postfix 日志显示,在RCPT TO:成功处理该行且收件人地址可接受后,Postfixsmtpd为该事务分配了一个队列 ID,并使用250 OK.

但是,DATASMTP 客户端并没有发出命令,而是发出了RSET重置/中止当前邮件事务的命令,Postfix 回复了250 OK.

我对这个命令的作用做了一些研究,不出所料,简单邮件传输协议 RFC 2821提供了最全面的信息:

此命令指定将中止当前邮件事务。任何存储的发送者、接收者和邮件数据必须被丢弃,并清除所有缓冲区和状态表。接收方必须向不带参数的 RSET 命令发送“250 OK”回复。客户端可以随时发出重置命令。如果在 EHLO 之后立即发布、在会话中发布 EHLO 之前、在数据结束指示符已发送和确认之后或在 QUIT 之前立即发布,则它实际上等效于 NOOP(即,如果没有效果)。SMTP 服务器不得因接收到 RSET 而关闭连接;该操作是为 QUIT 保留的(参见第 4.1.1.10 节)。

由于 EHLO 意味着服务器进行一些额外的处理和响应,因此 RSET 通常比重新发出该命令更有效,即使形式语义相同。

在某些情况下,与本规范的意图相反,SMTP 服务器可能会收到一个指示,表明底层 TCP 连接已关闭或重置。为了保持邮件系统的健壮性,SMTP 服务器应该为这种情况做好准备,并且应该将其视为在连接消失之前已收到 QUIT。

以上所有事情都发生在一秒钟之内,所以不应该有任何超时问题。

在下一秒,客户端发送了另一个,RSET但客户端在重新启动之前等待了整整 10 秒MAIL FROM:RCPT TO:但这次它继续DATA执行并发出命令并且事务完成(根据日志,所有这些都在同一秒内)。

本质上,我想知道为什么 SMTP 客户端会通过发出RSET命令而不是命令来中断自己的事务DATA

笔记:

  1. 我可以编辑问题以包括从邮件日志文件中提取的内容,但是通过-v调试,它们非常冗长,我不想用大量无关数据淹没人们。

  2. 我搜索了 sSMTP 源代码,但没有发现任何提及RSET.

Ant*_*gan 14

TLDR

我一直想知道为什么 SMTP 客户端会通过发出 RSET 命令而不是 DATA 命令来中断它自己的事务。简短的回答是它不会。这是 SMTP 连接被防病毒软件拦截的症状。

客户端日志

我在 sSMTP 的配置中启用了调试选项,但我花了一些时间来弄清楚如何在 Cygwin 中安装和配置 syslog,以便将来自 Cygwin 进程的消息记录到/var/log/messages而不是 Windows 事件查看器。

但是,它只记录了第一个MAIL FROM:RCPT TO:命令;没有迹象表明这些命令被发送过一次以上——或者sSMTP曾经发送过一个RSET命令。

正如我的问题中提到的,我已经检查了 sSMTP 源代码,但没有发送RSET命令的代码。

赛门铁克拦截 SMTP 流量

用户 masegaloeh 建议防病毒软件可能正在修改 SMTP 数据包 - 他是对的:我暂时禁用了我计算机上的Symantec Endpoint Protection,SMTP 事务正常进行。

重新启用 Symantec Endpoint Protection 后,我使用Windows SysinternalsTCPView实用程序监视 TCP 连接,我可以看到 Symantec进程正在代理目标端口为 25 的所有 TCP 流量。ccSvcHst.exe

我 telnet 到邮件服务器上的端口 25(netcat 无法正常工作,可能是由于赛门铁克拦截)以通过手动输入 SMTP 命令发送测试邮件。同时,我打开了另一个终端窗口,通过 SSH 连接到邮件主机,同时运行sudo tail -F /var/log/maillog以同时监视 SMTP 服务器所看到的内容。

赛门铁克代理执行的拦截是微妙的。从邮件客户端的角度来看,几乎没有迹象表明它没有直接与 SMTP 服务器通信。大多数命令在发送时都会传递到邮件服务器,并且响应是您所期望的。直到我输入DATA命令,赛门铁克代理才开始改变:它的响应是:

354 Please start mail input.
Run Code Online (Sandbox Code Playgroud)

这看起来很正常,但实际上,来自我的 Postfix 服务器的响应应该是

354 End data with <CR><LF>.<CR><LF>
Run Code Online (Sandbox Code Playgroud)

另外:它实际上并没有将DATA命令传递给 Postfix,直到我完成消息正文并在后面加上QUIT.

注意:当我输入测试消息的内容时,赛门铁克代理通过发出NOOP命令来保持与 Postfix 服务器的连接。

处理有问题的 SMTP 客户端

我在我的问题中提到,我的最终目标是对我的组织使用的专有(二进制、闭源)邮件客户端进行故障排除。我发现这个客户端真的有问题:它发送了一个无效的HELO命令(没有任何主机名),然后在 SMTP 服务器礼貌地通知它语法错误后简单地放弃并退出——即使我已经将 Postfix 配置为不需要HELO(有效或其他)。

我通过安装带有 CentOS 7 的新服务器解决了这个问题,该服务器带有足够新的 Postfix 版本,以便我可以完全禁用 postfix HELO 检查(类似于 MS Exchange 如何简单地忽略无效的 HELO 命令)。


RSET的一般用途

我也一直想知道RSETSMTP 事务中的一般用法,我在 SMTP 的原始RFC 821 中发现了以下内容:

此命令指定要中止当前邮件事务。必须丢弃任何存储的发件人、收件人和邮件数据,并清除所有缓冲区和状态表。接收者必须发送一个 OK 回复。

RSET如果结果证明收件人电子邮件地址是针对不存在的用户,则将使用。以下 SMTP 事务是中止 SMTP 事务方案的示例。

R: 220 MIT-Multics.ARPA Simple Mail Transfer Service Ready
S: HELO ISI-VAXA.ARPA
R: 250 MIT-Multics.ARPA

S: MAIL FROM:<Smith@ISI-VAXA.ARPA>
R: 250 OK

S: RCPT TO:<Jones@MIT-Multics.ARPA>
R: 250 OK

S: RCPT TO:<Green@MIT-Multics.ARPA>
R: 550 No such user here

S: RSET
R: 250 OK

S: QUIT
R: 221 MIT-Multics.ARPA Service closing transmission channel
Run Code Online (Sandbox Code Playgroud)

重用 SMTP 连接

SMTP 客户端可以使用相同的 SMTP 连接将多条消息发送到同一目的地。此功能被Postfix称为SMTP 连接缓存。使用此性能功能时,PostfixRSET在每个MAIL FROM命令之前发送一个以验证 SMTP 连接仍然可用(请参阅Postfix 连接缓存)。