跳到主要内容

5. Electronic Mail -- SMTP and RFC-822 (电子邮件)

5. 电子邮件 -- SMTP 与 RFC-822 (ELECTRONIC MAIL -- SMTP and RFC-822)​

5.1 简介 (INTRODUCTION)​

在 TCP/IP 协议套件中, 电子邮件采用 RFC-822 [SMTP:2] 规定的格式, 并使用 RFC-821 [SMTP:1] 定义的简单邮件传输协议 (SMTP) 传输.

虽然 SMTP 多年来保持不变, 但互联网社区在 SMTP 的使用方式上做了若干改变. 特别是, 向域名系统 (DNS) 的转换引起了地址格式与邮件路由的变化. 在本节中, 我们假定读者熟悉 DNS 的概念与术语, 其要求见第 6.1 节.

RFC-822 规定了电子邮件消息的互联网标准格式. RFC-822 取代了较旧的标准 RFC-733, 后者虽然已过时, 但可能仍在少数地方使用. 这两种格式有时简单地按编号称呼 ("822" 与 "733").

RFC-822 也用于某些使用不同于 SMTP 的邮件传输协议的非互联网邮件环境, SMTP 本身也被改造用于某些非互联网环境. 注意, 本文档给出的只是互联网环境下使用 SMTP 与 RFC-822 的规则; 使用这些协议的其他邮件环境可以预期有它们自己的规则.

5.2 协议逐步分析 (PROTOCOL WALK-THROUGH)​

本节同时覆盖 RFC-821 与 RFC-822.

RFC-821 中的 SMTP 规范清晰并包含大量示例, 实现者应当不难理解. 本节只是更新或注解 RFC-821 的部分内容, 以符合当前用法.

RFC-822 是一份又长又密的文档, 定义了丰富的语法. 遗憾的是, 不完整或有缺陷的 RFC-822 实现很常见. 事实上, RFC-822 的众多格式几乎全部都在实际使用, 因此一个实现通常需要识别并正确解释全部 RFC-822 语法.

5.2.1 SMTP 模型 (The SMTP Model): RFC-821 第 2 节​

讨论 (DISCUSSION):

邮件通过客户端 (即 "发送方 SMTP", sender-SMTP) 与服务器 (即 "接收方 SMTP", receiver-SMTP) 之间的一系列请求/响应事务发送. 这些事务传递 (1) 消息本体 (由头部与正文组成), 以及 (2) SMTP 源与目的地址, 称为 "信封" (envelope).

SMTP 程序类似于 X.400 的消息传输代理 (MTA). 还会有更贴近最终用户的另一层协议软件, 负责构造与分析 RFC-822 消息头部; 该组件在 X.400 中称为 "用户代理" (User Agent), 本文档也使用这一术语. 用户代理与 SMTP 实现之间存在清晰的逻辑区分, 因为它们运行在不同的协议层次上. 但是请注意, 这种区分未必精确反映典型互联网邮件实现的结构. 通常有一个称为 "mailer" 的程序, 它既实现 SMTP, 也实现一部分用户代理功能; 其余用户代理功能则包含在用于撰写与阅读邮件的用户界面中.

SMTP 信封在始发地构造, 通常由用户代理在消息首次排入 Sender-SMTP 程序队列时完成. 信封地址可以派生自消息头部中的信息, 由用户界面提供 (例如实现 bcc: 请求), 或派生自本地配置信息 (例如邮件列表的展开). 一般而言, SMTP 信封无法在消息投递的后续阶段从头部重新推导, 因此信封使用 SMTP 的 MAIL 与 RCPT 命令与消息本体分开传输.

RFC-821 的正文暗示邮件要投递给某台主机上的单个用户. 随着域名系统以及使用邮件交换 (MX) 资源记录的邮件路由的出现, 实现者现在应当把邮件投递理解为投递给某个域上的用户, 该域可以是也可能不是特定主机. 这并不改变 SMTP 是主机到主机邮件交换协议这一事实.

5.2.2 规范化 (Canonicalization): RFC-821 第 3.1 节​

Sender-SMTP 在 MAIL 与 RCPT 命令中发送的域名必须 (MUST) 已经被 "规范化" (canonicalized), 即它们必须是完全限定的主体名称 (principal name) 或域字面量 (domain literal), 而不是昵称或域缩写. 规范化后的名称要么直接标识一台主机, 要么是一个 MX 名称; 它不能是 CNAME.

5.2.3 VRFY 与 EXPN 命令 (VRFY and EXPN Commands): RFC-821 第 3.3 节​

receiver-SMTP 必须实现 (MUST) VRFY, 并应当 (SHOULD) 实现 EXPN (本要求覆盖 RFC-821). 然而, 可以 (MAY) 有配置信息在特定安装中停用 VRFY 与 EXPN; 这甚至可以允许对选定的邮件列表停用 EXPN.

为 VRFY 命令定义了一个新的应答码:

              252 Cannot VRFY user (e.g., info is not local), but will
take message for this user and attempt delivery.

讨论 (DISCUSSION):

SMTP 用户和管理员经常使用这些命令来诊断邮件投递问题. 随着多级邮件列表展开 (有时超过两级) 的使用增多, EXPN 对诊断无意造成的邮件环路越来越重要. 另一方面, 有人认为 EXPN 构成重大的隐私乃至安全暴露.

5.2.4 SEND, SOML 与 SAML 命令 (SEND, SOML, and SAML Commands): RFC-821 第 3.4 节​

SMTP 可以 (MAY) 实现向用户终端发送消息的命令: SEND, SOML 与 SAML.

讨论 (DISCUSSION):

有人指出, 经由 MX 记录的邮件中继与 SEND 立即直接向用户终端投递消息的意图不一致. 不过, 无法直接写入用户终端的 SMTP 接收方, 可以在 SEND 之后的 RCPT 返回 "251 User Not Local" 应答, 把可能延后投递的情况告知发件方.

5.2.5 HELO 命令 (HELO Command): RFC-821 第 3.5 节​

sender-SMTP 必须 (MUST) 确保 HELO 命令中的 <domain> 参数是客户端主机的有效主体主机域名. 这样, receiver-SMTP 就不必为验证 HELO 参数而对该名称做 MX 解析.

HELO 接收方可以 (MAY) 验证 HELO 参数是否确实对应发送方的 IP 地址. 但是, 即使发送方的 HELO 命令未通过验证, 接收方也禁止 (MUST NOT) 拒绝接受消息.

讨论 (DISCUSSION):

验证 HELO 参数需要一次域名查询, 因此可能耗费相当长的时间. 下面 (见 "DATA Command") 建议了另一种追踪虚假邮件来源的工具.

另请注意, HELO 参数仍被要求具有有效的 <domain> 语法, 因为它会出现在 Received: 行中; 否则应发送 501 错误.

实现 (IMPLEMENTATION):

当 HELO 参数验证失败时, 建议的规程是在消息头部 (例如 "Received:" 行) 中插入一条关于发送者真实性未知的注记.

5.2.6 邮件中继 (Mail Relay): RFC-821 第 3.6 节​

我们区分三种类型的邮件 (存储) 转发:

(1) 简单转发器或 "邮件交换器" (mail exchanger) 凭借关于收件人的私有知识转发消息; 见 RFC-821 第 3.2 节.

(2) SMTP 邮件 "中继" (relay) 作为显式源路由 (定义于 RFC-821 第 3.6 节) 的结果, 在 SMTP 邮件环境内部转发消息. SMTP 中继功能使用 RFC-822 的 "@...:" 形式源路由 (见下文第 5.2.19 节).

(3) 邮件 "网关" (gateway) 在不同环境之间传递消息. 邮件网关的规则在下文第 5.3.7 节讨论.

正在转发消息但不是通往不同邮件环境的网关的互联网主机 (即它属于 (1) 或 (2) 情形), 不应当 (SHOULD NOT) 改动任何既有头部字段, 尽管该主机会按第 5.2.8 节的要求添加一条恰当的 Received: 行.

Sender-SMTP 不应当 (SHOULD NOT) 发送包含使用 "@...:" 地址形式的显式源路由的 RCPT TO: 命令. 因此, 不应使用 RFC-821 第 3.6 节定义的中继功能.

讨论 (DISCUSSION):

其意图是抑制一切源路由, 并在互联网环境内的邮件投递中废除显式源路由. 源路由是不必要的; 简单的目标地址 "user@domain" 应当总是足够. 这是一项明确的架构决定的结果: 邮件使用通用命名而非源路由. 这样, SMTP 提供端到端连接, DNS 提供全局唯一, 与位置无关的名称. MX 记录处理了原本可能需要源路由的主要场景.

receiver-SMTP 必须 (MUST) 接受信封中的显式源路由语法, 但它可以 (MAY) 实现 RFC-821 第 3.6 节定义的中继功能. 如果它不实现中继功能, 它应当 (SHOULD) 尝试把消息直接投递给最右 "@" 符号右侧的主机.

讨论 (DISCUSSION):

例如, 假设一台不实现中继功能的主机收到带有 SMTP 命令 "RCPT TO:<@ALPHA,@BETA:joe@GAMMA>" 的消息, 其中 ALPHA, BETA 与 GAMMA 代表域名. 该主机应当尝试用 "RCPT TO:<joe@GAMMA>" 直接把消息转发给 GAMMA, 而不是像 RFC-821 第 20 页建议的那样立即以 550 错误应答拒绝该消息. 由于该主机不支持中继, 它不要求更新反向路径.

有人提出, 为了在故障时手动路由邮件, 偶尔可能需要源路由; 然而, 这一需求的真实性与重要性存在争议. 为此目的使用显式 SMTP 邮件中继是不被鼓励的, 而且事实上可能不成功, 因为许多主机系统不支持它. 有人曾为此目的使用 "%-hack" (见第 5.2.16 节).

5.2.7 RCPT 命令 (RCPT Command): RFC-821 第 4.1.1 节​

支持 receiver-SMTP 的主机必须 (MUST) 支持保留邮箱 "Postmaster".

receiver-SMTP 可以 (MAY) 在 RCPT 参数到达时验证它们; 但是, RCPT 响应禁止 (MUST NOT) 延迟到超出合理时间 (见第 5.3.2 节).

因此, 对 RCPT 的 "250 OK" 响应不一定意味着投递地址有效. 在消息被接受之后发现的错误, 将通过向适当地址邮寄通知消息来报告 (见第 5.3.3 节).

讨论 (DISCUSSION):

能够立即验证 RCPT 参数的条件集合是一个工程设计选择. 在邮件传输之前把目标邮箱错误报告给 Sender-SMTP 通常符合期望, 可以节省时间与网络带宽; 但如果 RCPT 验证耗时过长, 这一优势就丧失了.

例如, 接收方可以立即验证任何简单的本地引用, 例如单个本地注册的邮箱. 另一方面, "合理时间" 的限制一般意味着把邮件列表的验证推迟到消息传输并被接受之后, 因为验证一个大型邮件列表可能耗时极长. 实现可以选择推迟或推迟验证那些非本地因而需要 DNS 查询的地址. 如果执行了 DNS 查询但发生了软性域名系统错误 (例如超时), 则必须假定其有效.

5.2.8 DATA 命令 (DATA Command): RFC-821 第 4.1.1 节​

每个 receiver-SMTP (而不仅仅是 "为中继或最终投递而接受消息" 的那一个 [SMTP:1]) 都必须 (MUST) 在消息开头插入一行 "Received:". 在 RFC-821 中称为 "时间戳行" (time stamp line) 的这一行中:

  • FROM 字段应当 (SHOULD) 同时包含 (1) HELO 命令中呈现的源主机名, 以及 (2) 从 TCP 连接确定的, 含源 IP 地址的域字面量.

  • ID 字段可以 (MAY) 按 RFC-822 的建议包含一个 "@", 但这不是必需的.

  • 当给出了多个 RCPT 命令时, FOR 字段可以 (MAY) 包含一个 <path> 条目列表.

互联网邮件程序禁止 (MUST NOT) 改变先前加入消息头部的 Received: 行.

讨论 (DISCUSSION):

在 Received: 行中同时包含源主机与 IP 源地址, 可能提供足以追踪非法邮件来源的信息, 从而消除显式验证 HELO 参数的需要.

Received: 行主要供人类追踪邮件路由之用, 主要用于故障诊断. 另见 5.3.7 下的讨论.

当 receiver-SMTP 对消息进行 "最终投递" (final delivery) 时, 它必须 (MUST) 把 SMTP 信封中的 MAIL FROM: 地址随消息一起传递, 以备日后必须发送错误通知消息时使用 (见第 5.3.3 节). 从互联网网关到不同邮件环境时有类似要求; 见第 5.3.7 节.

讨论 (DISCUSSION):

注意, 对 DATA 命令的最终应答只取决于消息的成功传输与存储. 目标地址的任何问题要么 (1) 已在 SMTP 对 RCPT 命令的错误应答中报告, 要么 (2) 在稍后邮寄给发件人的错误消息中报告.

实现 (IMPLEMENTATION):

MAIL FROM: 信息可以作为参数传递, 也可以作为插入消息开头的 Return-Path: 行传递.

5.2.9 命令语法 (Command Syntax): RFC-821 第 4.1.2 节​

RFC-821 中 MAIL FROM: 命令展示的语法遗漏了空路径的情形: "MAIL FROM: <>" (见 RFC-821 第 15 页). 必须支持 (MUST) 空反向路径.

5.2.10 SMTP 应答 (SMTP Replies): RFC-821 第 4.2 节​

receiver-SMTP 应当 (SHOULD) 只发送 RFC-821 第 4.2.2 节或本文档中列出的应答码. receiver-SMTP 应当 (SHOULD) 在合适时使用 RFC-821 示例中展示的文本.

sender-SMTP 必须 (MUST) 仅依据应答码 (251 与 551 应答除外) 而非文本决定其行动; 任何文本, 包括完全没有文本, 都必须是可接受的. 应答码之后的空格被视为文本的一部分. 只要可能, sender-SMTP 应当 (SHOULD) 只测试应答码的第一位数字, 如 RFC-821 附录 E 所规定.

讨论 (DISCUSSION):

使用 RFC-821 第 4.3 节未显式列出, 但按附录 E 阐述的应答码理论属于合法的应答码的 SMTP 系统, 曾引发互操作性问题.

5.2.11 透明性 (Transparency): RFC-821 第 4.5.2 节​

实现者必须 (MUST) 确保其邮件系统始终添加和删除句点, 以保证消息透明性.

5.2.12 MX 处理中 WKS 的使用 (WKS Use in MX Processing): RFC-974, 第 5 页​

RFC-974 [SMTP:3] 曾建议查询域名系统中的 WKS ("Well-Known Service") 记录, 以验证每个候选邮件目标确实支持 SMTP. 后续经验表明 WKS 并未得到广泛支持, 因此 MX 处理中的 WKS 步骤不应当 (SHOULD NOT) 使用.

以下是对 RFC-822 的注记, 按该文档的章节组织.

5.2.13 RFC-822 消息规范 (RFC-822 Message Specification): RFC-822 第 4 节​

Return-path 行展示的语法遗漏了空返回路径的可能性, 后者用于防止错误通知的循环 (见第 5.3.3 节). 完整语法是:

            return = "Return-path"  ":" route-addr
/ "Return-path" ":" "<>"

可选头部字段的集合在此扩展为包括 RFC-1049 [SMTP:7] 定义的 Content-Type 字段. 该字段 "让邮件阅读系统能够自动识别结构化消息体的类型, 并据此处理以供显示". [SMTP:7] 用户代理可以 (MAY) 支持该字段.

5.2.14 RFC-822 日期与时间规范 (RFC-822 Date and Time Specification): RFC-822 第 5 节​

日期的语法在此改为:

            date = 1*2DIGIT month 2*4DIGIT

所有邮件软件应当 (SHOULD) 在日期中使用 4 位年份, 以便向下一个世纪平稳过渡.

使用数字时区指示符的趋势很强, 实现应当 (SHOULD) 使用数字时区而非时区名称. 然而, 所有实现必须 (MUST) 接受两种记法. 如果使用时区名称, 它们必须 (MUST) 与 RFC-822 中定义的完全一致.

RFC-822 对军用时区的规定是错误的: 它们从 UT 起算的方向数错了 (符号反了). 因此, RFC-822 头部中的军用时区不携带任何信息.

最后, 注意附录 D 语法摘要中 "zone" 的定义有一个笔误; 正确定义在 RFC-822 第 3 节.

5.2.15 RFC-822 语法变更 (RFC-822 Syntax Change): RFC-822 第 6.1 节​

RFC-822 中 "mailbox" 的语法定义在此改为:

            mailbox =  addr-spec            ; simple address
/ [phrase] route-addr ; name & addr-spec

也就是说, 路由地址前面的短语 (phrase) 现在是可选的 (OPTIONAL). 这一变更使如下头部字段成为合法, 例如:

5.2.16 RFC-822 本地部分 (RFC-822 Local-part): RFC-822 第 6.2 节​

基本邮箱地址规格的形式为: "local-part@domain". 这里 "local-part" (有时称为地址的 "左手边") 是依赖于域的.

正在转发消息但不是右侧 "domain" 所暗示的目的主机的主机, 禁止 (MUST NOT) 解释或修改地址的 "local-part".

当邮件要从互联网邮件环境网关到外部邮件环境时 (见第 5.3.7 节), 该外部环境的路由信息可以 (MAY) 嵌入地址的 "local-part" 之中. 网关随后将按外部邮件环境恰当地解释这个本地部分.

讨论 (DISCUSSION):

尽管互联网内部不鼓励源路由 (见第 5.2.6 节), 但存在一些投递机制确实依赖源路由的非互联网邮件环境. 当邮件穿越互联网时, 域外环境的源路由通常可以埋在地址的 "local-part" 之中 (见第 5.2.16 节). 当邮件到达合适的互联网邮件网关时, 网关将解释该本地部分, 并为目标邮件环境构造必要的地址或路由.

例如, 互联网主机可能把邮件发往: "a!b!c!user@gateway-domain". 复杂的本地部分 "a!b!c!user" 在互联网域内不被解释, 但可以被指定的邮件网关解析和理解.

嵌入的源路由有时使用 "%" 作为右结合的路由操作符编码在 "local-part" 中. 例如, 在:

                 user%domain%relay3%relay2@relay1

中, "%" 约定意味着邮件要从 "relay1" 经 "relay2", "relay3" 路由, 最终到达 "domain" 上的 "user". 这就是通常所说的 "%-hack". 建议 "%" 的优先级低于隐藏在本地部分中的任何其他路由操作符 (例如 "!"); 例如, "a!b%c" 会被解释为 "(a!b)%c".

只有目标主机 (在本例中是 "relay1") 被允许分析本地部分 "user%domain%relay3%relay2".

5.2.17 域字面量 (Domain Literals): RFC-822 第 6.2.3 节​

mailer 必须能够 (MUST) 接受并解析内容 ("dtext"; 见 RFC-822) 为点分十进制主机地址的互联网域字面量. 这满足了第 2.1 节在邮件情形下的要求.

SMTP 必须接受 (MUST) 并识别对应其自身任何 IP 地址的域字面量.

5.2.18 常见地址格式错误 (Common Address Formatting Errors): RFC-822 第 6.1 节​

遗憾的是, 822 地址的格式化或解析错误很常见. 本节只提及最常见的错误. 用户代理必须 (MUST) 接受所有有效的 RFC-822 地址格式, 并且禁止 (MUST NOT) 生成非法地址语法.

  • 一个常见错误是漏掉组标识符 (group identifier) 后面的分号.

  • 一些系统在生成的消息中未能完全限定域名. 头部地址字段中 "@" 符号右侧必须是 (MUST) 完全限定的域名.

    例如, 一些系统未能完全限定 From: 地址; 这会使用户界面中的 "reply" 命令无法自动构造返回地址.

    讨论 (DISCUSSION):

    尽管 RFC-822 允许在一个域内部本地使用缩写域名, 但 RFC-822 在互联网邮件中的应用不允许这样做. 其意图是: 互联网主机禁止发送在地址字段中含缩写域名的 SMTP 消息头部. 这使得头部的地址字段能够不经修改地穿越互联网传输, 如第 5.2.6 节所要求.

  • 一些系统解析诸如这样的多跳显式源路由时出错:

                  @relay1,@relay2,@relay3:user@domain.
  • 一些系统过度限定域名, 在地址或 message-id 中的部分或全部域名后面加上尾点. 这违反 RFC-822 语法.

5.2.19 显式源路由 (Explicit Source Routes): RFC-822 第 6.2.7 节​

互联网主机软件不应当 (SHOULD NOT) 创建含有带显式源路由地址的 RFC-822 头部, 但必须 (MUST) 接受这类头部, 以与较早的系统兼容.

讨论 (DISCUSSION):

用一种保守的说法, RFC-822 说 "不鼓励使用显式源路由". 许多主机错误地实现了 RFC-822 源路由, 因此该语法在实践中无法无歧义地使用. 许多用户觉得该语法丑陋. 为投递之目的, 邮件信封中不需要显式源路由; 见第 5.2.6 节. 出于所有这些原因, 使用 RFC-822 记法的显式源路由不得用于互联网邮件头部.

如第 5.2.16 节所述, 有必要允许把显式源路由埋在地址的本地部分中 (例如使用 "%-hack"), 以便把邮件网关到另一个需要显式源路由的环境. 警觉的读者会注意到: 当目的地在互联网内部时, 用户代理没有办法检测并阻止这种隐式源路由的使用. 我们只能抑制互联网内部的任何形式的源路由, 因为它既无必要也不可取.

5.3 具体问题 (SPECIFIC ISSUES)​

5.3.1 SMTP 排队策略 (SMTP Queueing Strategies)​

主机 SMTP 实现的常见结构包括: 用户邮箱, 一个或多个在途消息排队区, 以及一个或多个用于收发邮件的守护进程. 具体结构将视主机上用户的需求以及主机所支持邮件列表的数量与规模而变化. 我们描述几种已被证明有益的优化, 特别是对支持高流量水平的 mailer 而言.

任何排队策略必须 (MUST) 包括:

  • 对所有活动设置超时. 见第 5.3.2 节.

  • 绝不以错误消息响应错误消息.

5.3.1.1 发送策略 (Sending Strategy)​

sender-SMTP 的一般模型是一个或多个周期性地尝试发送出站邮件的进程. 在典型系统中, 撰写消息的程序有某种方法为新发出的邮件请求立即处理, 而无法立即传输的邮件必须 (MUST) 排队并由发送方周期性重试. 邮件队列条目不仅包含消息本身, 还包含信封信息.

在一次尝试失败后, 发送方必须 (MUST) 延迟对特定目的地的重试. 一般而言, 重试间隔应当 (SHOULD) 至少 30 分钟; 然而, 当 sender-SMTP 能够确定无法投递的原因时, 更精致可变的策略将更有益.

重试持续进行, 直到消息被传输或发送方放弃; 放弃时间一般需要至少 4-5 天. 重试算法的参数必须 (MUST) 可配置.

发送方应当 (SHOULD) 维护一个它无法到达的主机列表及相应的超时, 而不是仅仅重试排队的邮件项.

讨论 (DISCUSSION):

经验表明故障通常是瞬时的 (目标系统崩溃了), 因此倾向于这样的策略: 消息在队列中的第一个小时内尝试连接两次, 然后退避到每两三个小时一次.

sender-SMTP 可以通过与 receiver-SMTP 的合作来缩短排队延迟. 特别是, 如果从某个特定地址收到了邮件, 那就是排队发往该主机的邮件现在可以发送的有力证据.

由于一台主机有多个地址 (见第 5.3.4 节), 该策略还可以进一步调整, 以在投递时间与资源使用之间取得优化.

sender-SMTP 可能针对每台不可达的目的主机积累大量排队的消息, 如果它在每个重试周期重试所有这些消息, 将造成过高的互联网开销, 且守护进程会被长时间阻塞. 注意, SMTP 一般只能在一分钟或更长的超时之后才能断定一次投递尝试已失败; 如果每个连接都一分钟超时, 并对几十甚至几百条排队消息重复之, 将造成极大的延迟.

当同一条消息要投递给同一台主机上的多个用户时, 该消息只应当 (SHOULD) 传输一份副本. 也就是说, sender-SMTP 应当使用命令序列: RCPT, RCPT,... RCPT, DATA, 而不是序列: RCPT, DATA, RCPT, DATA,... RCPT, DATA. 强烈敦促实现这一效率特性.

类似地, sender-SMTP 可以 (MAY) 支持多个并发的出站邮件事务, 以实现及时投递. 然而, 应当 (SHOULD) 施加某种限制, 以保护主机不把全部资源都投入邮件.

多宿主主机不同地址的使用在下文讨论.

5.3.1.2 接收策略 (Receiving strategy)​

receiver-SMTP 应当 (SHOULD) 尝试在 SMTP 端口上始终保持一个挂起的监听 (pending listen). 这将要求支持多个入 TCP 连接用于 SMTP. 可以 (MAY) 施加某种限制.

实现 (IMPLEMENTATION):

当 receiver-SMTP 从某个特定主机地址收到邮件时, 它可以通知 sender-SMTP 重试发往该主机地址的所有待发邮件.

5.3.2 SMTP 中的超时 (Timeouts in SMTP)​

sender-SMTP 的超时有两种方法: (a) 分别限制每条 SMTP 命令的时间, 或 (b) 限制单条邮件消息的整个 SMTP 对话的时间. sender-SMTP 应当 (SHOULD) 使用方案 (a), 每命令超时. 超时应当 (SHOULD) 易于重新配置, 最好无需重新编译 SMTP 代码.

讨论 (DISCUSSION):

超时是 SMTP 实现的必备特性. 如果超时太长 (更糟的是没有超时), 互联网通信故障或 receiver-SMTP 程序的软件缺陷会把 SMTP 进程无限期地占住. 如果超时太短, 在消息投递中途超时的尝试会浪费资源.

如果使用方案 (b), 超时必须非常大, 例如一小时, 以便为展开非常大的邮件列表留出时间. 超时可能还需要随消息大小线性增长, 以计入传输超大消息所需的时间. 一个大的固定超时带来两个问题: 故障仍可能把发送方占用很长时间, 而超大消息仍可能虚假超时 (这是一种浪费的失败!).

使用推荐的方案 (a), 为每条 SMTP 命令以及数据传输的每个缓冲设置一个计时器. 后者意味着总体超时天然与消息大小成正比.

基于与繁忙邮件中继主机打交道的丰富经验, 每命令超时的最小值应当 (SHOULD) 如下:

  • 初始 220 消息: 5 分钟

    Sender-SMTP 进程需要区分 TCP 连接失败与接收初始 220 问候消息的延迟. 许多 receiver-SMTP 会接受 TCP 连接, 但延迟交付 220 消息, 直到其系统负载允许处理更多邮件.

  • MAIL 命令: 5 分钟

  • RCPT 命令: 5 分钟

    如果邮件列表与别名的处理不推迟到消息被接受之后, 则需要更长的超时.

  • DATA 发起: 2 分钟

    这是在等待对 DATA 命令的 "354 Start Input" 应答期间.

  • 数据块: 3 分钟

    这是在等待传输一块数据的每个 TCP SEND 调用完成期间.

  • DATA 终止: 10 分钟.

    这是在等待 "250 OK" 应答期间. 当接收方收到终止消息数据的最终句点时, 它通常要执行处理, 把消息投递到用户邮箱. 此时的一次虚假超时将非常浪费, 因为消息已经成功发送.

receiver-SMTP 在等待来自发送方的下一条命令时, 应当 (SHOULD) 有至少 5 分钟的超时.

5.3.3 可靠的邮件接收 (Reliable Mail Receipt)​

当 receiver-SMTP 接受一封邮件时 (通过发送 "250 OK" 消息响应 DATA), 它就承担了投递或中继该消息的责任. 它必须严肃对待这一责任, 即禁止 (MUST NOT) 因为轻率的原因丢失消息, 例如主机随后崩溃, 或可预见的资源短缺.

如果在消息被接受之后发生投递失败, receiver-SMTP 必须 (MUST) 拟写并邮寄一条通知消息. 该通知必须 (MUST) 使用信封中空的反向路径 (" <>") 发送; 见 RFC-821 第 3.6 节. 该通知的收件人应当 (SHOULD) 是信封返回路径中的地址 (或 Return-Path: 行). 然而, 如果该地址为空 (" <>"), receiver-SMTP 禁止 (MUST NOT) 发送通知. 如果该地址是显式源路由, 它应当 (SHOULD) 被削减到最后一跳.

讨论 (DISCUSSION):

例如, 假设必须为一条以 "MAIL FROM:<@a,@b:user@d>" 到达的消息发送错误通知. 通知消息应发送至: "RCPT TO: <user@d>".

SMTP 接受消息之后的一些投递失败是不可避免的. 例如, 由于 "软性" 域名系统错误, 或因为目标是一个邮件列表 (见前文对 RCPT 的讨论), receiver-SMTP 可能无法验证 RCPT 命令中的全部投递地址.

为了避免因超时而收到重复消息, receiver-SMTP 必须 (MUST) 力求把响应终止消息传输的最终 "." 所需的时间降到最少. 关于这一问题的讨论见 RFC-1047 [SMTP:4].

5.3.4 可靠的邮件传输 (Reliable Mail Transmission)​

为传输消息, sender-SMTP 从信封中的目的地址确定目标主机的 IP 地址. 具体而言, 它把 "@" 符号右侧的字符串映射为一个 IP 地址. 这一映射或传输本身可能以软错误失败, 此时 sender-SMTP 将按第 5.3.1.1 节的要求把出站邮件重新排队, 稍后重试.

当映射成功时, 由于 (a) 多条 MX 记录, (b) 多宿主, 或两者兼有, 该映射的结果可能是一列备选投递地址而非单个地址. 为提供可靠的邮件传输, sender-SMTP 必须 (MUST) 能够按顺序尝试 (并重试) 该列表中的每个地址, 直到某次投递尝试成功. 然而, 也可以 (MAY) 对可尝试的备选地址数量施加可配置的限制. 无论如何, 主机应当 (SHOULD) 至少尝试两个地址.

以下信息用于给主机地址排序:

(1) 多条 MX 记录 -- 它们含有应用于排序的优先级指示. 如果存在多个优先级相同的目的地, 且没有明确的理由偏爱某一个 (例如按地址偏好), 那么 sender-SMTP 应当 (SHOULD) 随机挑选一个, 以把负载分散到特定组织的多个邮件交换机上; 注意, 这是对 [DNS:3] 中规程的改进.

(2) 多宿主主机 -- 目标主机 (可能取自首选的 MX 记录) 可能是多宿主的, 此时域名解析器将返回一列备选 IP 地址. 域名解析器接口 (见下文第 6.1.3.4 节) 有责任把该列表按偏好递减排序, 而 SMTP 必须按呈现的顺序尝试它们.

讨论 (DISCUSSION):

尽管要求具备尝试多个备选地址的能力, 但某些特定安装可能希望限制或停用备选地址的使用. 发送方是否应当使用多宿主主机的不同地址进行重试, 一直存在争议. 使用多个地址的主要理由是它最大化了及时投递 (有时是任何投递) 的概率; 反对的理由是它可能导致不必要的资源使用.

注意, 资源使用也在很大程度上由第 5.3.1 节讨论的发送策略决定.

5.3.5 域名支持 (Domain Name Support)​

SMTP 实现必须 (MUST) 使用第 6.1 节定义的机制在域名与 IP 地址之间做映射. 这意味着每个互联网 SMTP 都必须包含 (MUST) 对互联网 DNS 的支持.

特别地, sender-SMTP 必须支持 (MUST) MX 记录方案 [SMTP:3]. 关于 SMTP 的域名支持, 另见 [DNS:2] 第 7.4 节.

5.3.6 邮件列表与别名 (Mailing Lists and Aliases)​

支持 SMTP 的主机应当 (SHOULD) 同时支持别名 (alias) 与列表 (list) 两种地址展开形式以实现多重投递. 当消息被投递或转发到展开后的列表形式中的每个地址时, 信封中的返回地址 ("MAIL FROM:") 必须改为 (MUST) 管理该列表的人的地址, 但消息头部必须保持不变 (MUST); 特别是, 消息的 "From" 字段不受影响.

讨论 (DISCUSSION):

一项重要的邮件设施是这样一种机制: 通过变换或 "展开" 一个伪邮箱地址, 把单条消息投递到多个目的地. 当消息发送到这样一个伪邮箱 (有时称为 "exploder") 时, 副本被转发或重新分发到展开列表中的每个邮箱. 我们按展开规则把这类伪邮箱归类为 "别名" 或 "列表":

(a) 别名 (Alias)

展开别名时, 接收方 mailer 只是把信封中的伪邮箱地址依次替换为各个展开后的地址; 信封的其余部分与消息正文保持不变. 然后消息被投递或转发到每个展开后的地址.

(b) 列表 (List)

可以说, 邮件列表靠 "再分发" (redistribution) 而非 "转发" (forwarding) 运作. 展开列表时, 接收方 mailer 把信封中的伪邮箱地址依次替换为各个展开后的地址, 并改变信封中的返回地址, 使得最终投递产生的所有错误消息都返回给列表管理员, 而不是消息发起人 -- 后者通常无法控制列表的内容, 且往往会对错误消息感到恼火.

5.3.7 邮件网关 (Mail Gatewaying)​

在不同邮件环境 (即不同的邮件格式与协议) 之间网关化邮件是复杂的, 不易标准化. 例如见 [SMTP:5a], [SMTP:5b]. 然而, 对于互联网与其他邮件环境之间的网关, 可以给出一些一般性要求.

(A) 当消息跨越邮件环境边界被网关化时, 头部字段可以 (MAY) 在必要时重写.

讨论 (DISCUSSION):

这可能涉及解释目标地址的本地部分, 如第 5.2.16 节所建议.

网关到互联网的其他邮件系统一般使用 RFC-822 头部的一个子集, 但其中一些没有与 SMTP 信封等价的东西. 因此, 当消息离开互联网环境时, 可能需要把 SMTP 信封信息折叠进消息头部. 一种可能的解决方案是创建新的头部字段来承载信封信息 (例如 "X-SMTP-MAIL:" 与 "X-SMTP-RCPT:"); 然而, 这将要求改动外部环境中的邮件程序.

(B) 把消息转发进或转发出互联网环境时, 网关必须前置 (MUST) 一行 Received:, 但禁止 (MUST NOT) 以任何方式改动头部中已有的 Received: 行.

讨论 (DISCUSSION):

这一条是第 5.2.8 节一般 "Received:" 行要求的一个子集; 在此重申以示强调.

来自其他环境的消息的 Received: 字段可能不完全符合 RFC822. 然而, Received: 行最重要的用途是调试邮件故障, 而这种调试会被那些好心试图 "修复" Received: 行的网关严重妨碍.

强烈鼓励网关在它提供的 Received 字段的 "via" 子句中指明环境与协议.

(C) 从互联网一侧看, 网关应当 (SHOULD) 接受 SMTP 命令与 RFC-822 头部中的所有有效地址格式, 以及所有有效的 RFC-822 消息. 尽管网关必须接受 (MUST) RFC-822 头部或信封中的 RFC-822 显式源路由 ("@...:" 格式), 它可以 (MAY) 对该源路由采取行动, 也可以不采取; 见第 5.2.6 与 5.2.19 节.

讨论 (DISCUSSION):

为了简化向远程环境地址的转换, 人们常常很想限制邮件网关接受的地址范围. 这种做法基于一个假设: 邮件用户能够控制其 mailer 发送给邮件网关的地址. 但实践中, 用户几乎无法控制最终发送的地址; 他们的 mailer 可以随意把地址改成任何合法的 RFC-822 格式.

(D) 网关必须 (MUST) 确保它转发进互联网的消息的所有头部字段都满足互联网邮件的要求. 特别是, "From:", "To:", "Cc:" 等字段中的所有地址必须 (必要时) 经变换以满足 RFC-822 语法, 并且它们必须对发送回复有效且有用.

(E) 用于把邮件从互联网协议转换到另一环境的协议的翻译算法, 应当 (SHOULD) 尽力确保来自外部邮件环境的错误消息被投递到 SMTP 信封的返回路径, 而不是 RFC-822 消息 "From:" 字段中列出的发送者.

讨论 (DISCUSSION):

互联网邮件列表通常把列表维护者的地址放进信封, 但保持原始消息头部不变 ("From:" 字段含原始发送者). 这产生了普通收件人所期望的行为: 对头部的回复发往原始发送者而非列表维护者; 而错误则发往维护者 (他能修问题) 而非发送者 (他多半修不了).

(F) 类似地, 把消息从另一环境转发进互联网时, 网关应当 (SHOULD) 依据外部环境提供的错误消息返回地址 (如果有) 来设置信封返回路径.

5.3.8 最大消息长度 (Maximum Message Size)​

mailer 软件必须能够 (MUST) 收发至少 64K 字节长 (含头部) 的消息, 并且强烈期望支持大得多的最大尺寸.

讨论 (DISCUSSION):

尽管 SMTP 没有定义消息的最大长度, 许多系统都施加了实现限制.

目前互联网中的事实最小限制是 64K 字节. 然而, 电子邮件的用途多种多样, 会产生大得多的消息. 例如, 邮件常被用来代替 FTP 传输 ASCII 文件, 特别是传输整个文档. 因此, 消息可能达到 1 兆字节甚至更大. 我们注意到, 本文档与其低层配套文档合计就有 0.5 兆字节.

5.4 SMTP 要求摘要 (SMTP REQUIREMENTS SUMMARY)​

                                               |          | | | |S| |
                                               |          | | | |H| |F
                                               |          | | | |O|M|o
                                               |          | |S| |U|U|o
                                               |          | |H| |L|S|t
                                               |          |M|O| |D|T|n
                                               |          |U|U|M| | |o
                                               |          |S|L|A|N|N|t
                                               |          |T|D|Y|O|O|t
FEATURE                                        |SECTION   | | | |T|T|e
-----------------------------------------------|----------|-|-|-|-|-|--
                                               |          | | | | | |
RECEIVER-SMTP:                                 |          | | | | | |
  Implement VRFY                               |5.2.3     |x| | | | |
  Implement EXPN                               |5.2.3     | |x| | | |
    EXPN, VRFY configurable                    |5.2.3     | | |x| | |
  Implement SEND, SOML, SAML                    |5.2.4     | | |x| | |
  Verify HELO parameter                         |5.2.5     | | |x| | |
    Refuse message with bad HELO                |5.2.5     | | | | |x|
  Accept explicit src-route syntax in env.      |5.2.6     |x| | | | |
  Support "postmaster"                          |5.2.7     |x| | | | |
  Process RCPT when received (except lists)     |5.2.7     | | |x| | |
      Long delay of RCPT responses              |5.2.7     | | | | |x|
                                               |          | | | | | |
  Add Received: line                            |5.2.8     |x| | | | |
      Received: line include domain literal     |5.2.8     | |x| | | |
  Change previous Received: line                |5.2.8     | | | | |x|
  Pass Return-Path info (final deliv/gwy)       |5.2.8     |x| | | | |
  Support empty reverse path                    |5.2.9     |x| | | | |
  Send only official reply codes                |5.2.10    | |x| | | |
  Send text from RFC-821 when appropriate       |5.2.10    | |x| | | |
  Delete "." for transparency                   |5.2.11    |x| | | | |
  Accept and recognize self domain literal(s)   |5.2.17    |x| | | | |
                                               |          | | | | | |
  Error message about error message             |5.3.1     | | | | |x|
  Keep pending listen on SMTP port              |5.3.1.2   | |x| | | |
  Provide limit on recv concurrency             |5.3.1.2   | | |x| | |
  Wait at least 5 mins for next sender cmd      |5.3.2     | |x| | | |
  Avoidable delivery failure after "250 OK"     |5.3.3     | | | | |x|
  Send error notification msg after accept      |5.3.3     |x| | | | |
    Send using null return path                 |5.3.3     |x| | | | |
    Send to envelope return path                |5.3.3     | |x| | | |
    Send to null address                        |5.3.3     | | | | |x|
    Strip off explicit src route                |5.3.3     | |x| | | |
  Minimize acceptance delay (RFC-1047)          |5.3.3     |x| | | | |
-----------------------------------------------|----------|-|-|-|-|-|--

                                               |          | | | | | |
SENDER-SMTP:                                   |          | | | | | |
  Canonicalized domain names in MAIL, RCPT      |5.2.2     |x| | | | |
  Implement SEND, SOML, SAML                    |5.2.4     | | |x| | |
  Send valid principal host name in HELO        |5.2.5     |x| | | | |
  Send explicit source route in RCPT TO:        |5.2.6     | | | |x| |
  Use only reply code to determine action       |5.2.10    |x| | | | |
  Use only high digit of reply code when poss.  |5.2.10    | |x| | | |
  Add "." for transparency                      |5.2.11    |x| | | | |
                                               |          | | | | | |
  Retry messages after soft failure             |5.3.1.1   |x| | | | |
    Delay before retry                          |5.3.1.1   |x| | | | |
    Configurable retry parameters               |5.3.1.1   |x| | | | |
    Retry once per each queued dest host        |5.3.1.1   | |x| | | |
  Multiple RCPT's for same DATA                 |5.3.1.1   | |x| | | |
  Support multiple concurrent transactions      |5.3.1.1   | | |x| | |
    Provide limit on concurrency                |5.3.1.1   | |x| | | |
                                               |          | | | | | |
  Timeouts on all activities                    |5.3.1     |x| | | | |
    Per-command timeouts                        |5.3.2     | |x| | | |
    Timeouts easily reconfigurable              |5.3.2     | |x| | | |
    Recommended times                           |5.3.2     | |x| | | |
  Try alternate addr's in order                 |5.3.4     |x| | | | |
    Configurable limit on alternate tries       |5.3.4     | | |x| | |
    Try at least two alternates                 |5.3.4     | |x| | | |
  Load-split across equal MX alternates         |5.3.4     | |x| | | |
  Use the Domain Name System                    |5.3.5     |x| | | | |
    Support MX records                          |5.3.5     |x| | | | |
    Use WKS records in MX processing            |5.2.12    | | | |x| |
-----------------------------------------------|----------|-|-|-|-|-|--
                                               |          | | | | | |
MAIL FORWARDING:                               |          | | | | | |
  Alter existing header field(s)                |5.2.6     | | | |x| |
  Implement relay function: 821/section 3.6     |5.2.6     | | |x| | |
    If not, deliver to RHS domain               |5.2.6     | |x| | | |
  Interpret 'local-part' of addr                |5.2.16    | | | | |x|
                                               |          | | | | | |
MAILING LISTS AND ALIASES                      |          | | | | | |
  Support both                                  |5.3.6     | |x| | | |
  Report mail list error to local admin.        |5.3.6     |x| | | | |
                                               |          | | | | | |
MAIL GATEWAYS:                                 |          | | | | | |
  Embed foreign mail route in local-part        |5.2.16    | | |x| | |
  Rewrite header fields when necessary          |5.3.7     | | |x| | |
  Prepend Received: line                        |5.3.7     |x| | | | |
  Change existing Received: line                |5.3.7     | | | | |x|
  Accept full RFC-822 on Internet side          |5.3.7     | |x| | | |
  Act on RFC-822 explicit source route          |5.3.7     | | |x| | |
  Send only valid RFC-822 on Internet side      |5.3.7     |x| | | | |
  Deliver error msgs to envelope addr           |5.3.7     | |x| | | |
  Set env return path from err return addr      |5.3.7     | |x| | | |
                                               |          | | | | | |
USER AGENT -- RFC-822                          |          | | | | | |
  Allow user to enter <route> address           |5.2.6     | | | |x| |
  Support RFC-1049 Content Type field           |5.2.13    | | |x| | |
  Use 4-digit years                             |5.2.14    | |x| | | |
  Generate numeric timezones                    |5.2.14    | |x| | | |
  Accept all timezones                          |5.2.14    |x| | | | |
  Use non-num timezones from RFC-822            |5.2.14    |x| | | | |
  Omit phrase before route-addr                 |5.2.15    | | |x| | |
  Accept and parse dot.dec. domain literals     |5.2.17    |x| | | | |
  Accept all RFC-822 address formats            |5.2.18    |x| | | | |
  Generate invalid RFC-822 address format       |5.2.18    | | | | |x|
  Fully-qualified domain names in header        |5.2.18    |x| | | | |
  Create explicit src route in header           |5.2.19    | | | |x| |
  Accept explicit src route in header           |5.2.19    |x| | | | |
                                               |          | | | | | |

注: 以上要求摘要表为英文原文原样保留 (列含义: MUST / SHOULD / MAY / SHOULD NOT / MUST NOT / FOOTNOTE).