摘要

  • 即使接收端已经处于请求列出的字符集,RFC 2066 也要求它明确回复 ACCEPTED;沉默不再能被当作成功状态。
  • 客户端与服务器同时发出 CHARSET REQUEST 时,服务器必须拒绝客户端的请求,客户端则必须回答服务器的请求,以角色不对称结束碰撞。
  • ACCEPTED 证明某个请求已被收到,并选定后续文本使用的字符集;它不证明后续字节正确、翻译无误、应用已处理、用户已认证或会话安全。

设想一个 Telnet 端点提出两种字符集,随后没有收到任何消息。对方也许早已使用其中一种编码,因此沿用旧规则保持沉默;请求也许丢了;对方也许根本没有正确实现子协商。三种情况在请求方眼里完全一样:没有回执。

1997 年 1 月发布的 Experimental RFC 2066 不接受这种模糊状态。它为 Telnet 定义了编号 42 的 CHARSET 选项,让客户端和服务器能够明确后续文本的编码,并可选择交换翻译表。真正改变互操作边界的,不是字符集名单有多长,而是切换必须留下可以观察的终局回答。

上层防循环规则到了子协商层并不够用

RFC 854 用 DO、DON'T、WILL 和 WON'T 构成对称的 Telnet 选项协商。如果双方同时要求启用一个选项,各自通常可以把对方的请求视为对自己请求的肯定回答。为了避免礼貌变成无限循环,文件又规定:不要确认一个已经生效的模式,但真正改变模式的请求必须收到回应。

RFC 855 把参数讨论放在基础选项同意之后。双方先用 DO/WILL 确认愿意讨论某个选项,再在 IAC SB 和 IAC SE 之间交换参数。两层解决的是两个问题。DO CHARSET 和 WILL CHARSET 只说明可以开始谈,却没有决定下一个文本字节采用哪套编码。

RFC 2066 因而明确拒绝在字符集请求上沿用“已满足便不回答”的解释。若接收端已经以请求列表中的某套字符集收发文本,它仍必须发出 ACCEPTED,不得忽略。文件直接说明理由是确定性:请求方不应等待超时再推测。与此同时,ACCEPTED 无须再次确认,所以明确回答不会重新启动确认循环。

请求是一份有顺序的提案,不是单方命令

只有既收到 DO CHARSET、又发送过 WILL CHARSET 的一方,才有资格发出 CHARSET REQUEST,两项动作的先后不限。请求中可以列出一项或多项字符集,通常按偏好排序。名称若不以私用的 X- 开头,就必须在 IANA 登记;接收端仍可根据自己的能力与偏好选择列表中的某一项。

接收端因此有四条封闭路径:确认已经使用的字符集;接受另一项可支持的字符集;在请求方允许时发送翻译表;或者在一项也无法支持时发出 REJECTED。肯定回答必须点明列表中的名称,否定回答则确认已经收到请求,但拒绝本轮列出的全部选择。

ACCEPTED 与 REJECTED 都结束当前子协商。它们不是泛化的成功或故障标签。前者使后续文本承担使用指定编码的义务,后者则确保本轮提案未生效。二者都不解释能力从何而来,也不禁止下一轮提出新选择。

两个同时请求必须指定一方先退

当两个合格端点都在收到对方消息前发出 CHARSET REQUEST,对称性反而造成僵局。新的请求不能作为旧请求的合法回答;如果没有额外规则,双方都会握着对方的请求,等待自己那一项得到终局回应。

RFC 2066 用稳定角色打破对称:服务器必须以负面确认结束客户端的请求;客户端必须回答服务器发来的请求。这样,一条提案先被关闭,另一条提案继续走向 ACCEPTED、REJECTED 或翻译表路径。规则没有宣称服务器偏好更高贵,只是让两个端点对同一碰撞计算出相同的下一步。

文件还允许拒绝之后继续谈。服务器若更希望客户端适配服务器应用的字符集,可以先拒绝客户端提案,再提出自己的列表;若客户端也拒绝,服务器还可回到客户端原先能处理的选择。但每一轮都必须各自收尾,偏好可以改变,回执边界不能合并。

编码切换点就在字节流中

ACCEPTED 之后,双方必须用指定字符集编码后续文本。子协商进行期间,数据应进入队列,待协商终止后才按正确字符集发送。确认消息因此也是排序标记:它把仍受旧约定支配的字节,与开始受新约定支配的字节隔开。

它的范围很窄。翻译只作用于文本,不作用于 Telnet 命令,并且只有在 BINARY 模式中才发生;没有 BINARY 时,数据仍按 NVT ASCII 解释。对于块模式终端,RFC 建议配合 End of Record 选项,让接收方仍能看清记录边界。选中字符集并没有替换周围的 Telnet 规则。

翻译表也有自己的回执链。TTABLE-ACK 确认表格接收成功并启动映射后的编码;TTABLE-NAK 请求重发,但若多次失败,应转入 TTABLE-REJECTED 或 CHARSET REJECTED,而不是无限消耗双方。设计反复选择了同一原则:失败可以被看见,但不能靠耐心耗尽才算结束。

回执有价值,正因为它没有越权

抓到一条 ACCEPTED,可以支持一个精确结论:对端收到了某个请求,并从请求列表中选择了一套名称,用于之后的文本。它不能证明之后的字节真的符合编码、翻译表无误、应用成功解码、用户任务完成。一条 REJECTED 证明的是本轮收到并拒绝,也不等于对端永远没有能力。

该选项更没有安全光环。RFC 2066 的 Security Considerations 只说安全问题未作讨论。字符集协商不会认证端点、授权应用、加密会话或保护文本完整性。状态转移可以完全确定,承载它的通道仍可能不安全。

出版记录也不能替代运行证据。RFC 2066 是 Experimental,IANA 当前仍把 42 号登记为 CHARSET。这些事实证明文件和参数分配存在,却不能说明部署数量、互操作率或任何具名产品采用了碰撞规则。

若用 Lu Heng 后来的“最小初始规范”框架回看,这是一种紧凑的共同层:只规定请求资格、终局回答、字节切换点和双方可本地执行的冲突规则。这是后来的编辑解释,不是 RFC 作者意图的证据。更稳妥的历史结论是:当沉默可能代表多种事实,互操作首先要把缺失的回执变成协议消息。

来源