摘要

  • FEAT 让 FTP 客户端在不逐条试运行扩展命令的情况下,取得服务器声明支持的扩展清单;OPTS 则为尚未执行的目标命令选择行为。
  • 对实现了 FEAT 的合规服务器,成功清单必须完整列出已支持的规范扩展;但 500 或 502 不能反证扩展不存在,因为早期服务器可能先实现扩展、后错过清单机制。
  • 能力声明、选项接受、身份与权限、命令终局回复、数据连接以及最终文件状态,是六类不同证据。

“试试看”并不是无害的读操作

FTP 的基础命令来自 RFC 959,协议寿命远长于任何一次软件发布。后来出现的新命令、新参数和新安全机制,不可能在同一天进入所有服务器。客户端知道某份规范存在,并不等于知道眼前这台服务器已经实现它。

最直接的探测方式,是把候选命令逐条发出,观察服务器回什么。RFC 2389 指出,这种办法不仅增加流量,还可能产生不希望发生的效果。问题在于命令的语义:它本来是请求服务器做事,而不是请求服务器描述自己。用行动探测知识,会让探测本身成为风险来源。

FEAT 提供了一个独立的描述面。客户端只发送一个不带参数的词,服务器返回所支持扩展的结构化清单。客户端再把这份清单与自己的理解范围比较,决定下一步。公共层只规定清单的外壳;每项扩展仍由自己的规范定义标签、参数和具体效果。

这不是把决定权交给清单,而是让决定发生在行动之前。服务器描述本地能力,客户端自行判断是否采用,真正的命令随后再接受本地策略与运行状态检验。

一个空格为什么值得写进规范

有扩展可列时,服务器以多行 211- 回复开头,每项能力各占一行,并且必须以一个空格起始,最后用 211 End 结束。这个空格不是排版装饰。它保证能力行不会被误判成结束行,为解析器划出明确边界。

首行说明文字可以自由书写,能力行却必须遵守语法。标签往往像命令名,但并非必须如此;它也可以代表某种机制或属性。标签之后的参数由相应扩展规范解释。返回顺序没有含义,同一服务器两次回答时也无需维持同一顺序。

RFC 2389 还要求客户端容忍未知标签。较老客户端看到未来扩展时,不应把整份回复判为错误。它只需忽略自己不理解的部分,继续使用双方都懂的子集。这个约束把协议演进从“全懂或全失败”改成了可选择的兼容。

清单里不需要列出 FEAT 自己;收到非 500、非 502 的回复,已经证明服务器理解它。OPTS 也不列,因为实现 FEAT 的服务器必须同时实现 OPTS。清单只承担特定职责,不重复已经由交互证明的事实。

成功回复可以闭合,失败回复不能反推

RFC 2389 最精细的设计,是对“有”和“没有”采用不同证明标准。

只要服务器实现 FEAT,它就必须把 RFC 959 与 RFC 2389 之外、自己实际支持且有正式文档的 FTP 扩展全部列出。因此,一份合规的成功清单具有完备性:某项扩展若不在其中,客户端可以认为这台服务器不支持它。

但服务器若返回 500 或 502,只能证明它不认识 FEAT,不能证明它没有任何扩展。原因来自部署时间线。许多扩展早于 1998 年出现,旧服务器可能已经实现其中一项,却从未实现后来才定义的清单命令。此时,客户端为了兼容,仍可能单独测试那项旧扩展。

甚至“认识 FEAT,但没有扩展可报”也保留了这种模糊性。规范建议返回单行 211,却允许 500 或 502,因为对客户端而言,这与完全不认识 FEAT 几乎无法区分。

这形成了单向闭合:成功且合规的清单能消除清单范围内的不确定性;没有取得清单,只能留下未知,不能凭空制造“没有”。协议没有让历史沉默承担它从未承诺过的语义。

OPTS 改的是下一步,不是结果

能力并不总是开或关。有些扩展允许客户端选择返回内容或处理方式。OPTS 为这种选择提供统一命令形式:指定目标命令,再附上该命令规范定义的选项。

200 表示目标命令和选项都被识别并可接受;501 表示在状态不变时重复也不会成功的永久错误;451 表示服务器当前的临时条件阻止了接受。无论哪一种,都只说明选项交涉,不说明后续工作已经完成。

RFC 3659 的 MLST 是清楚的实例。服务器可在 FEAT 的 MLST 行中列出它能生成的文件事实,并用星号标明默认返回项。客户端发出 OPTS MLST 后,可以改变后续 MLST 与 MLSD 的事实集合。规范还提醒,一些非默认事实对服务器而言可能很昂贵,不应只为美化显示而随意索取。

于是,“支持某事实”“默认返回它”“客户端请求它”“它适用于这个文件”“服务器实际返回它”成为五个阶段。若系统只保存一个“支持”布尔值,就把选择、成本和具体结果全部抹掉了。

AUTH TLS 出现在清单中,并不等于连接已受保护

RFC 4217 把同一机制用于 FTP over TLS。支持该机制的 FEAT 服务器要列出 AUTH TLS、PBSZ 与 PROT。这些标签告诉客户端:这里存在一条可尝试的安全协商路径。它们并没有建立安全会话。

客户端仍须发送 AUTH TLS,服务器以 234 接受,双方完成 TLS 握手,随后设置保护参数,客户端按策略核验服务器证书身份,并完成所需的 FTP 用户认证。任何一步都可能失败。能力清单无法替证书作证,也不能替本地权限系统同意文件操作。

RFC 7151 定义的 HOST 也显示了相同边界。服务器支持虚拟主机时必须在清单中列出 HOST,但选择主机可能切换认证环境,所选名称仍需与证书身份对应。看到一扇门、选中一扇门、获得进入许可,并不是同一个事实。

IANA 登记的是名称边界,不是优劣裁决

扩展继续增加后,RFC 5797 建立 IANA 的 FTP Commands and Extensions 登记表,目的是避免不同机制占用同一命令或功能名称。登记项记录命令、FEAT 代码、说明、命令类型、实现要求与参考文档;历史项也保留,以免旧名称被重新使用。

RFC 5797 明确说明:登记并不证明一项扩展获得“批准”。满足永久公开规范,或者已经在普遍可获得的客户端和服务器中实现,均可能构成登记依据。表中的小写占位代码用于保留名称,也不意味着服务器应在 FEAT 中返回它们。

因此,登记表解决全局名称冲突;服务器清单描述本地实现;OPTS 记录本次选择;认证和策略决定此人此刻能做什么;命令回复与数据连接说明实际发生了什么。把登记表提升为运行真相,就会让薄协调层越权成为产品裁判。

披露能力,是为了减少更危险的探测

RFC 2389 承认,能力清单可能泄露服务器特征,观察者可据此推断更多信息。没有 FEAT 时,对方也能逐项探测命令,只是这种行为更容易在日志中留下可疑痕迹。作者认为这一区别不足以否定清单机制,同时要求各扩展自行处理其安全问题。

这里的选择不是“全公开”与“全保密”。它是在有限范围内公布稳定、可解析的能力描述,以减少用实际操作探测的必要。描述面提供选择所需的共同语义,执行面继续保留身份、策略、资源和结果的本地控制权。

RFC 2389 的历史意义,也正在这种克制里。它没有宣布“列出来就能用”,而是先回答“服务器声明会说哪种扩展语言”,再让运行中的交互回答“它是否愿意、是否能够、是否真的完成了这次请求”。清单是行动前的证据,不是行动后的凭证。

来源