我试图了解 Postfix 是如何处理电子邮件的——以及 SMTP 邮件交易的一些更详细的细节。我的短期目标是调试专有(二进制、闭源)SMTP 客户端,但我首先认为我会检查成功的 SMTP 事务中发生的情况。
我计划在我们的 LAN 防火墙上阻止传出 SMTP(端口 25),因此我将 Postfix 配置为内部邮件服务器,以接受来自(原始)本地客户端软件的邮件,该客户端软件只能通过未经身份验证的 SMTP(通过端口 25)发送电子邮件。
我通过附加详细标志打开 Postfixsmtpd
进程的调试,如Troubleshooting with Postfix Logs中所述。然后,我使用 Cygwin Mutt 和sSMTP(的最小实现)从我自己的工作站发送了一封电子邮件。-v
master.cf
sendmail
Postfix 日志显示,在RCPT TO:
成功处理该行并且收件人地址可以接受后,Postfixsmtpd
为该事务分配了一个队列 ID,并以250 OK
.
但是,DATA
SMTP 客户端没有发出命令,而是发出了一个RSET
重置/中止当前邮件事务的命令,而 Postfix 回复了一个250 OK
.
我对这个命令的作用做了一些研究,不出所料,简单邮件传输协议 RFC 2821提供了最全面的信息:
此命令指定当前邮件事务将被中止。必须丢弃任何存储的发件人、收件人和邮件数据,并清除所有缓冲区和状态表。接收者必须发送一个“250 OK”回复到一个不带参数的 RSET 命令。客户端可以随时发出重置命令。如果在 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
。
笔记:
我可以编辑问题以包括从邮件日志文件中提取的内容,但是通过
-v
调试,它们非常冗长,我不想用大量不相关的数据压倒人们。我搜索了 sSMTP 源代码,但没有找到任何提及
RSET
.
TLDR
我一直想知道为什么 SMTP 客户端会通过发出 RSET 命令而不是 DATA 命令来中断自己的事务。简短的回答是它不会。这是 SMTP 连接被防病毒软件拦截的症状。
客户端日志
我在 sSMTP 的配置中启用了 Debug 选项,但我花了一些时间才弄清楚如何在 Cygwin 中安装和配置 syslog,以便将来自 Cygwin 进程的消息记录到
/var/log/messages
而不是 Windows 事件查看器。但是,它只记录了第一个
MAIL FROM:
和RCPT TO:
命令;没有迹象表明这些命令被发送了不止一次——或者sSMTP
曾经发送过一个RSET
命令。正如我的问题中提到的,我检查了 sSMTP 源代码,但没有发送
RSET
命令的代码。Symantec 拦截 SMTP 流量
用户 masegaloeh 建议防病毒软件可能正在修改 SMTP 数据包 - 他是对的:我暂时禁用了计算机上的Symantec Endpoint Protection,并且 SMTP 事务正常进行。
重新启用 Symantec Endpoint Protection 后,我使用Windows Sysinternals中的TCPView实用程序监视 TCP 连接,我可以看到 Symantec进程正在代理目标端口为 25 的所有 TCP 流量。
ccSvcHst.exe
我远程登录到邮件服务器上的端口 25(netcat 无法正常工作,可能是由于赛门铁克拦截)通过手动输入 SMTP 命令发送测试邮件。同时,我在运行时打开了另一个终端窗口,其中包含与邮件主机的 SSH 连接,
sudo tail -F /var/log/maillog
以同时监视 SMTP 服务器所看到的内容。Symantec 代理执行的拦截是微妙的。从邮件客户端的角度来看,几乎没有迹象表明它没有直接与 SMTP 服务器通信。大多数命令在发送时都会传递到邮件服务器,并且响应是您所期望的。直到我输入
DATA
命令,赛门铁克代理才开始改变:它响应:这看起来很正常,但实际上,我的 Postfix 服务器的响应应该是
另外:
DATA
直到我完成消息正文并在其后加上QUIT
.NOOP
注意:当我输入测试消息的内容时,赛门铁克代理通过发出命令保持与 Postfix 服务器的连接处于活动状态。处理有问题的 SMTP 客户端
我在我的问题中提到,我的最终目标是对我的组织使用的专有(二进制、闭源)邮件客户端进行故障排除。我发现这个客户端确实有问题:它发送了一个无效的
HELO
命令(没有任何主机名),然后在 SMTP 服务器礼貌地通知它它的语法错误后简单地放弃并退出——即使我已经将 Postfix 配置为不需要HELO
(有效或其他)。我通过安装带有 CentOS 7 的新服务器解决了这个问题,该服务器带有足够新的 Postfix 版本,以便我可以完全禁用 postfix HELO 检查(类似于 MS Exchange 简单地忽略无效 HELO 命令的方式)。
RSET 的一般用途
我也一直想知道
RSET
SMTP 事务中的一般用法,我在 SMTP 的原始RFC 821中发现了以下内容:RSET
如果事实证明收件人电子邮件地址是给不存在的用户的,则将使用该地址。以下 SMTP 事务是Aborted SMTP Transaction Scenario的示例。重用 SMTP 连接
SMTP 客户端可以使用同一个 SMTP 连接将多条消息发送到同一个目的地。这个特性被 Postfix 称为SMTP 连接缓存。使用此性能功能时,Postfix
RSET
在每个MAIL FROM
命令之前发送一个以验证 SMTP 连接是否仍然可用(请参阅Postfix 连接缓存)。