摘要
- RFC 934 把以连字符开始的一行视为被封装邮件之间的潜在边界。为防止正文中的同类行被误切,转发方在其前加入
,解包方识别并去除这段本层添加的前缀。 - 该方案按层可逆,却不会让嵌套中的表示保持原长。同一行每经过一个新的转发层,就可能再多一个保护前缀。
- MIME 仍须解决部件边界,但改为声明一个不能出现在部件中的 boundary 值。RFC 2046 明说,RFC 934 的逐层连字符引用会令行增长,并会同 SMTP 的长行折叠发生冲突。
一个符号何时才有解释权
早期邮件并不是完全没有结构。RFC 822 以第一条空行划分头部与正文;在结构化字段中,引号、括号和反斜杠又各自承担引号或分隔的作用。关键不在于这些字符天生拥有命令权,而在于特定解析器在特定位置已进入了使用该语法的状态。
这一区分很容易被忽略。看起来像分隔符的字节,可能只是另一层正文中的普通文字。只有当读者已经确认自己正在读取哪个封套、应用哪套文法时,它才可被当成结构。若把上下文丢掉,数据就会被提升成控制指令。
RFC 934 为邮件转发提出两个角色:forwarding agent 在投递前写出外层草稿,bursting agent 在送达后把它拆开。外层正文可有引言、若干封被封装的信和结尾。其最初的 encapsulation boundary 定义很简单:一条以 开头的行。
这个约定让顺序拆分很直接。一条边界既可结束上一封内信,也可开始下一封;相邻的两条边界甚至表示空消息。但它马上碰到同形冲突。内信作者可能恰好写下一行 - 清单,并不想让它结束外层消息。若不作区分,解包方无法从字面形状分辨内容与自己的切分命令。
加入的不是新内容,而是一份本层凭证
RFC 934 注意到已有转发代理似乎没有处理这种情况,于是提出 character stuffing。转发代理在内文中发现一条以边界模式起始的行时,先输出一个连字符和一个空格,再输出原行。- 清单 在外层内部变成 - - 清单。
解包代理的规则与之配对。它在行首看到 时,不把它当作真实边界,而是输出后面的内容;只有看到真正的边界时,才结束当前内信并开始下一封。RFC 934 将这称为简单的字符填充,并说它允许递归转发。
这个设计的分寸在于它没有声称修改了原文的意义。外层代理没有成为那条清单的作者,也没有裁定原行本来是边界。它只给自己的解析器留下一份可辨认的本层凭证:这行不能由我这一层当作控制行。离开本层时,bursting agent 撤销这份凭证,行重新回到内信的语境。
RFC 822 对异构网络转换的描述提供相似的工程直觉:先撤除离开网络的特殊习惯,回到规范形态,再施加进入网络的本地习惯。两种机制并不相同,但都要求转换有来源、有范围、有逆操作。否则,临时的运输痕迹会沉淀成所谓原始内容。
能还原,不代表途中不变形
RFC 934 所说的递归是成立的,但成立的边界很窄。第一层把 - 清单 写成 - - 清单。第二层收到的行仍以连字符开始,于是再加本层的 ,外部形态变为 - - - 清单。以后每加一层,行就再长一些。每个解包层只应去除自己理解的那一个前缀;它无权替内部层清理看似相同的痕迹。
最终按相反顺序拆开时,原文能够回来。然而在真正的传送过程中,字节数已经增加。可逆性证明的是一对编码器和解码器在给定层数下能对上;它没有证明中间中继一定能容纳增长后的行、不会折行或规范化空白、不会让排障者失去“哪个层加了哪个前缀”的证据。
因此中间表示不是可以略去的尴尬细节。中继会传它,限制会作用于它,过滤器会检查它。一个设计若只保存最终恢复出的内容,却不保存封装声明、解析状态和转换记录,就无法说明途中真正发生了什么。
RFC 934 本身也没有假装规范一出,旧行为就消失。它的目标之一是尽量少改动已存在的转发代理。为兼容既有 bursting agent,它建议考虑在边界四周放空行;但又说明严格遵循本文的转发方不应生成这些空行。文档记录的是正在运行的兼容性场,而不是一间能够清除历史的实验室。
MIME 把“避免碰撞”的工作放到边界选择处
RFC 2045 说明 MIME 建立在 RFC 934、RFC 822 和 RFC 1049 等更早工作之上。它引入被 Content 字段描述的实体和 multipart 正文。这是一套供参与者采用的共同语法,而不是对所有旧邮件的追溯性裁决。
在 RFC 2046 中,multipart 实体在 Content-Type 中声明 boundary 参数。分隔行由两个连字符加这个值构成;该值不得在被封装部件中单独出现,也不得成为其中一行的前缀;嵌套的 multipart 还必须使用不同的值。责任因而转移到组装者:它要选择一个不会与内容相撞的边界。
这不意味着 boundary 获得了全局权力。它只因某个实体的头部声明和相应 multipart 解析状态而有效。一段相同字符出现在普通文本中,并不会因为肉眼像边界就有资格打开或关闭一个部件。
RFC 2046 对 RFC 934 的对比十分明确。两个连字符部分是为与旧方法保持粗略兼容,也便于某些实现搜索边界;但 multipart 不遵守 RFC 934 对内嵌连字符行的引用约定。理由不是审美:每一层引用都会让行变长,而某些 SMTP 实现又会包折长行。若要支持深层嵌套,这个中间形态本身就成为问题。
这并非宣判旧方案“错误”。RFC 934 以很小的状态机处理了真实的歧义,且给出了逆操作。MIME 面对的是更广的类型化、嵌套式正文,于是换了账本:不再反复改写每条可能碰撞的数据行,而是选择并声明一条不出现于数据中的边界。两种方案都不能免除对内容和传输形态的观察。
让语法权力在边界处失效
这个历史留下的好问题是:一个标记何时获得结构解释权,又在何时失去它?
RFC 934 的 只在外层语法中证明“不要把这条内层行当作我的边界”;解包后它被撤回。MIME 的 boundary 只在声明它的实体中有效。任何体系若让临时标记越过了这一范围,就会把运输状态误作内容、把缓存误作身份、把展示符号误作可执行命令。
应对办法不是赋予某个机构或格式无限解释权,而是把共同规则限定为可在本地验证的最小范围:说明封套、记录转换、让执行者只撤回自己负责的变化,并允许恢复原有形态。还要把重复组合列为一等测试项:表示会增长多少、在哪里遇到长度限制、哪种规范化会抹掉逆操作所需的证据。
来源与证据边界
封闭证据集为 RFC 822、RFC 934、RFC 2045 与 RFC 2046。它们支持本文所述历史语法、转义与解包规则,以及 MIME 明确写下的对比;它们不证明当代产品的行为、普遍部署、嵌套邮件的实际比例或任何特定解析器的安全性。
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
