摘要

  • RFC 2034 要求支持方在几乎所有 2xx、4xx、5xx SMTP 回复的自然语言文本之前加入 class.subject.detail,且增强码的首段必须与传统回复类别一致。
  • 服务器即使没有收到客户端的 EHLO,也要发送增强状态码;客户端既无须请求,也没有拒绝机制。RFC 把这定义为受旧文本槽位保护的特殊兼容选择,而不是绕过扩展协商的通用先例。
  • 增强码便于机器分类、重试和跨语言呈现,但只证明某台服务器在某次会话中声明了一个结果类别,不证明根因、最终投递、阅读、策略正当性或文本没有泄露内部信息。

“550”可以结束一次自动重试,却不能解释为什么结束。收件地址不存在、禁止转发、访问策略拒绝,可能都落在同一类传统回复里。人工阅读服务器文字或许能分辨,但文字会随软件、管理员和语言变化。以关键词搜“mailbox”的告警系统,换一台服务器或换一种语言就会丢掉含义。

1996 年 10 月发布的 RFC 2034 在这道缝隙里放进了一个很小的结构。传统三位 SMTP 回复码仍然存在,面向人的说明也仍然存在。不同之处是:说明文字的开头必须先出现 class.subject.detail。于是,同一行同时拥有粗粒度控制信号、细粒度机器分类和可自然表达的人类解释。

这不是把“真相”编码了。它只是让一项远端声明更容易被独立系统读取、比较和保存。

在人类文本槽位里再放一层机器语义

扩展名是 Enhanced-Status-Codes,EHLO 关键字是 ENHANCEDSTATUSCODES,关键字不带参数,也不增加 SMTP 动词。它改的是客户端本来就会收到的回复,而不是引入另一套命令流程。

增强码由三段组成。class 只能是 2、4 或 5;subject 与 detail 各是一到三位数字。class 必须与传统三位码的首位一致:2xx 只能配 2.X.X,4xx 配 4.X.X,5xx 配 5.X.X。服务器不能一边以传统码表示临时失败,一边以增强码把同一结果标成永久失败。

这条一致性规则让两套码成为叠加关系,而不是竞争关系。传统码继续控制 SMTP 状态机的大方向;subject 和 detail 则把同一大类拆得更细。后来 RFC 5321 对传统分工的表述很清楚:数字供自动程序使用,文字供人使用。RFC 2034 等于又把文字字段的起点交给机器,同时把后半段留给人。

实际价值来自可移植性。邮件系统可以区分同属永久失败的地址问题与策略拒绝,不必猜测自由文本;运营团队可以跨产品汇总同一类别;用户界面可以用本地语言解释,无须假定服务器一定说英语;重试、升级和工单分流也能记录自己依据的是哪个码。

但“一致”不等于“真实”。线上出现 550 5.1.1,能证明对端在那个时刻声明了永久性的目的邮箱问题。它不能独立查询对端数据库,不能证明策略制定者的身份,也不能保证另一条授权路径会得出相同结论。

“几乎所有回复”有精确例外

RFC 2034 覆盖 2xx、4xx 和 5xx 的回复行,但排除初始问候,以及对 HELO 和 EHLO 的回复。3xx 被明确排除。规范示例里的 354 是请客户端开始发送正文的中间回复,因此没有增强状态码;这不是遗漏,而是契约的一部分。

这组边界决定了怎样做合规检查。若探针要求每个 SMTP 数字码后都必须带三段码,它会把正确实现报成故障。若统计系统只数“缺失”,却不保存前一条命令和会话位置,它会把合法例外与真正缺失混在一起。

规范示例还展示了多种结构:发件人接受为 2.1.0,收件人接受为 2.1.5,不存在的邮箱被标为 5.1.1,禁止转发使用 5.7.1,接收邮件为 2.6.0,关闭会话为 2.0.0。这些例子证明三段命名空间可以贯穿会话,却不证明“接收”之后邮件一定进入收件箱,更不证明用户看见或读过。

客户端没有请求,服务器仍然启用

典型扩展先协商能力:服务器宣布,客户端选择,然后行为改变。RFC 2034 刻意不这么做。支持方不论客户端是否使用 EHLO,都要附上增强状态码;规范没有让客户端请求或拒绝的命令。

它的兼容逻辑是把变化限制在旧客户端本已必须容忍的文本字段内。旧实现可以把三段码和后面的句子都当作普通文字,不必发送新命令,也不必进入新状态。作者又认为当时 SMTP 错误码实现普遍不理想,因此让扩展客户端与非扩展客户端都获得更清晰的错误具有价值。

RFC 随即用强烈措辞封住外推:这是一个非常特殊的案例,绝不能成为未来扩展不经服务器宣布和客户端启用就显著改变交互的许可证。因此,RFC 2034 的教训不是“好功能可以免协商”,而是无请求变化必须被既有语法安全包裹,影响范围还必须足够小、足够可验证。

从最小初始规范的角度看,共享规则很克制:宣布支持;在规定的回复里发出合法三段码;保持类别一致;不破坏原 SMTP 对话。各方可以在本地验证这些属性,却无需把服务器没有公开的推理当作事实。

多行一致性让一项声明保持完整

SMTP 允许一个回复跨越多行。RFC 2034 要求每一行文本开头都出现同一个增强状态码。示例里的两行 551 都重复 5.7.1。RFC 5321 对外层传统码也提出相同要求:同一个多行回复的每一行必须使用同一三位码。

重复不是装饰。它防止一项回复在不同分行中声称互相冲突的结果,也让日志分片、逐行处理或不完整采样仍能识别声明类别。每一行的自然语言可以补充不同细节,机器分类不能漂移。

但是解释空间越大,暴露面也越大。RFC 2034 的安全考虑直接指出,服务器提供更多细节,必然让外界得到更多服务器信息,这些信息可能帮助规避安全措施。多行拒绝若写得过细,可能暴露转发拓扑、账户是否存在、内部规则名、阈值或访问控制路径。

因此,一致性与最小披露必须同时成立。该重复的稳定类别要重复,给人的说明要足以行动,但不能因为字段装得下,就向未知对端交付一份内部诊断报告。

状态码是证词,不是因果证明

增强状态码改善了接收方根据服务器证词采取行动的能力,却没有把证词变成对服务器内部的审计。

4 类声明可以进入临时失败队列,5 类声明可以按本地规则停止自动重试。subject 和 detail 可以把事件分给地址管理、容量、安全或配置团队。若同一对端在版本更新后改变了类别分布,长期记录也能暴露变化。这些用途成立,是因为它们只对已观测声明采取行动。

同一个码无法证明数据库查询为何失败,无法证明反滥用规则是否正确,也无法保证对端明天继续同一策略。2 类结果同样常被过度解释。“Message accepted”描述的是接收服务器的一次状态转换,不等于最终投递、进入收件箱、在设备上展示、被人打开或阅读。

规范示例随后构造了一份退信报告,却特意说明报告 MTA 为减少杂乱,从 diagnostic-code 字段中省略了增强状态码。这一细节揭示了证据链的脆弱性:实时回复、队列记录、后生成报告和面向用户的摘要是不同工件,每一步都可能保留、改写或丢弃信息。不能因为后来的消息语气确定,就假设它原样复现了会话。

RFC 5248 后来建立 IANA 注册流程,减少增强邮件状态码的含义冲突。注册协调的是命名空间,不是个案真实性。已注册的码仍可能被有缺陷的软件发错、关联到错误事件,或配上一段泄露过度的文字。

先保存线上事件,再解释世界

强证据记录应把层次分开:对端和时间、SMTP 命令、传统码、增强码、完整文字、多行边界、本地解析结果以及本地动作。语法、类别一致性和多行重复可以确定性验证;根因、策略正当性与最终结果通常不行。

Heng Lu 对现实层与符号层的区分在这里很有用。可执行事实是一串字节确实跨过连接,本地程序确实据此采取了动作。诊断短语则是对端对该事件的解释。RFC 2034 让这种解释更规整、更可比较,却没有赋予它裁定连接之外事实的权力。

这正是这项扩展的持久价值:它提供了独立系统共同验证的形状,同时拒绝假装故事已经完整。自动化可以信任这个形状去分类;调查可以保存这项声明去交叉验证;线缆没有说出的部分,必须继续标注为不确定。

出典