RFC: The Anytls Protocol Version 2 Category: Informational Date: April 2026
Anytls 协议规范版本 2 (The Anytls Protocol Version 2)¶
摘要 (Abstract)¶
本文档描述了 Anytls 协议,这是一种基于传输层安全性 (TLS) 运行的安全、多路复用的代理协议。该协议旨在通过动态填充方案提供强大的流量混淆以对抗流量分析,在单个会话中多路复用多个逻辑流,并通过回退到标准协议来防御主动探测。
1. 简介 (Introduction)¶
Anytls 是一种在标准 TLS 连接之上建立安全会话的代理协议。它由初始认证阶段和随后多路复用多个逻辑流的会话层组成。为了对抗高级的深度包检测 (DPI) 启发式算法,Anytls 利用动态的 paddingScheme(填充方案)来主动改变流量指纹。
2. 约定和术语 (Conventions and Terminology)¶
本文档中的关键词“必须”(MUST)、“绝对不能”(MUST NOT)、“要求”(REQUIRED)、“将”(SHALL)、“绝对不将”(SHALL NOT)、“应该”(SHOULD)、“不应该”(SHOULD NOT)、“建议”(RECOMMENDED)、“可以”(MAY) 和“可选”(OPTIONAL) 需按照 RFC 2119 中的描述进行解释。
- Session (会话): 运行 Anytls 会话层的单一已建立的 TLS 连接。
- Stream (流): 在会话中用于代理特定连接的多路复用逻辑通道。
- Client (客户端): 发起 Anytls 连接的软件。
- Server (服务端): 接收连接并执行代理的 Anytls 端点。
3. 协议架构 (Protocol Architecture)¶
整体协议栈的层级如下:
+-------------------------------------------------+
| 用户 TCP/UDP 代理 |
+-------------------------------------------------+
| Anytls 流层 (Stream) |
+-------------------------------------------------+
| Anytls 会话层 (Session) |
+-------------------------------------------------+
| 传输层安全性 (TLS) |
+-------------------------------------------------+
| 传输控制协议 (TCP) |
+-------------------------------------------------+
4. 认证阶段 (Authentication Phase)¶
TLS 握手完成之后,客户端必须立即发送认证请求。
4.1. 客户端认证请求¶
认证载荷的结构如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+ +
| sha256(password) (32 Bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| padding0 length | padding0 (可变长度) ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- sha256(password): 32 字节。预共享协议密码的 SHA-256 哈希值。
- padding0 length: 2 字节 (大端序 uint16)。后续填充数据的长度。
- padding0: 可变长度。随机填充数据。
注:认证部分的开销为 34 字节(不包含可变长度的填充)。
4.2. 服务端认证响应¶
服务端必须读出第一个数据包并校验认证请求(包括完整读出 padding0)。
- 如果认证成功,服务端进入会话循环。
- 如果认证失败,服务端必须立即关闭连接,或者回退 (fallback) 到标准的 HTTP/L7 服务以防御主动探测。
5. 会话层 (Session Layer)¶
认证完成后,双方进入会话事件循环。
5.1. 帧格式 (Frame Format)¶
会话层使用以下帧格式进行通信:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| command | streamId |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | data length | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data (可变长度) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- command: 1 字节 (uint8)。标识帧类型的命令。
- streamId: 4 字节 (大端序 uint32)。标识逻辑流。
- data length: 2 字节 (大端序 uint16)。载荷的长度。
- data: 可变长度的载荷。
5.2. 命令 (Commands)¶
定义了以下命令。除非下文明确规定,否则命令绝对不能携带数据载荷 (data)。
| 命令名称 | 值 | 引入版本 | 方向 | 描述 |
|---|---|---|---|---|
cmdWaste |
0 | v1 | 双向 | 填充。必须完整读出数据并无声丢弃。 |
cmdSYN |
1 | v1 | 客户端 -> 服务端 | 打开一个新流。 |
cmdPSH |
2 | v1 | 双向 | 向流推送数据。 |
cmdFIN |
3 | v1 | 双向 | 关闭流 (EOF 标记)。 |
cmdSettings |
4 | v1 | 客户端 -> 服务端 | 客户端发送设置。 |
cmdAlert |
5 | v1 | 服务端 -> 客户端 | 服务端发送的致命错误警告。 |
cmdUpdatePaddingScheme |
6 | v1 | 服务端 -> 客户端 | 动态填充方案更新。 |
cmdSYNACK |
7 | v2 | 服务端 -> 客户端 | 流打开确认。 |
cmdHeartRequest |
8 | v2 | 双向 | Keep-alive 心跳请求。 |
cmdHeartResponse |
9 | v2 | 双向 | Keep-alive 心跳响应。 |
cmdServerSettings |
10 | v2 | 服务端 -> 客户端 | 服务端发送设置。 |
5.2.1. cmdSettings (4)¶
客户端在开启新会话时必须立即发送 cmdSettings 帧。如果服务端在收到 cmdSettings 之前收到 cmdSYN,必须拒绝此次会话。
其 data 载荷为采用 UTF-8 编码的键值对,键与值之间用 = 连接,不同项目之间用换行符 \n 分割。
示例:
5.2.2. cmdServerSettings (10)¶
如果客户端上报的版本 v >= 2,服务端在收到 cmdSettings 后必须立即回复 cmdServerSettings。
示例:
5.2.3. cmdAlert (5)¶
载荷包含服务端发送的警告文本信息。客户端必须将其读出并打印到日志,然后双方必须关闭会话。服务端可以使用此命令来拒绝过时或不合规的客户端连接。
5.2.4. cmdUpdatePaddingScheme (6)¶
如果服务端检测到客户端的 padding-md5(来自 cmdSettings)与服务端当前方案不同,服务端必须发送此命令。载荷格式在第 6 节中描述。
5.2.5. cmdHeartRequest (8) 和 cmdHeartResponse (9)¶
任意一方收到 cmdHeartRequest 后,必须回复 cmdHeartResponse。这些命令用于检测并恢复卡住的隧道连接。
5.3. 流生命周期命令 (Stream Lifecycle Commands)¶
5.3.1. cmdSYN (1)¶
通知服务端打开一条新的流。客户端必须为每个流生成在会话内单调递增的 streamId。
5.3.2. cmdSYNACK (7)¶
对于版本 v >= 2 的客户端,服务端收到 cmdSYN 后,应该在代理出站连接 TCP 握手完成后,发送带有对应 streamId 的 cmdSYNACK。
- 若不带有 data,则表示代理流握手成功。
- 若带有 data,则 data 代表错误信息。客户端收到错误信息后必须关闭对应的流。
5.3.3. cmdPSH (2)¶
数据载荷承载该流的实际代理传输数据。
5.3.4. cmdFIN (3)¶
通知对端关闭指定 streamId 的流。
- 当会话正常时,本端收到 cmdFIN 并关闭本地流后,不需要向对端回复 cmdFIN。
- 当会话已经关闭时,不需要发送 cmdFIN。
6. 动态填充方案 (Dynamic Padding Scheme)¶
填充方案规定了如何对数据包进行分片和填充,以混淆流量特征。
6.1. 填充方案格式¶
该方案作为换行符分隔的字符串发送。示例:
6.2. 方案指令 (Scheme Directives)¶
stop: 例如,stop=8表示只处理前 8 个数据包(索引 0 到 7)的填充方案。0=X-Y: 第 0 个数据包(认证期间的padding0)的规则。该包不支持分片。客户端发送长度在 X 和 Y 字节之间的填充。1及以上: 会话阶段数据包的规则。
数据包计数 (Packet Counting):
数据包索引以底层 TLS 连接上调用 Write() 的次数为准。
- 数据包 1 通常包括:cmdSettings + 首个流的 cmdSYN + cmdPSH(包含代理目标地址)。
- 数据包 2 通常包含来自用户的首个代理数据块(例如被代理连接的 TLS ClientHello)。
分片和填充逻辑 (Fragmentation and Padding Logic):
如果数据包由 A-B,C-D 规则控制:
1. 用户数据被分片。第一个分块的尺寸在 A 和 B 之间随机选择。第二个在 C 和 D 之间,依此类推。(尺寸指 TLS PlainText 长度,不计算 TLS 加密等开销)。
2. 如果所有指定的分片发送完之后,用户数据仍有剩余,则直接原生发送剩余数据。
3. 如果在所有分片发送完之前,用户数据已发送完毕,则端点必须发送充满填充(建议用 0)的 cmdWaste 以满足尺寸要求。
4. 检查符号 (c): 如果存在 ,c, 分隔符,并且上一个分片发送完毕后用户数据已无剩余,则实现必须立即从 Write 操作返回,绝对不发送 c 之后定义的后续填充包。
6.3. 方案生命周期 (Scheme Lifecycle)¶
- 客户端必须在 Client 对象中存储作用于连接到该服务端的
paddingScheme。 - 客户端第一次连接必须使用默认的
paddingScheme。 - 收到
cmdUpdatePaddingScheme后,客户端在后续与该服务端新建的会话中必须使用下发的新方案。这确保了当默认方案的特征被封锁时,只有极少部分初始连接会暴露特征。
7. 连接复用 (Connection Multiplexing)¶
客户端必须使用连接池实现会话层复用功能。总体架构为:
TCP Proxy -> Stream -> Session -> TLS -> TCP
7.1. 复用策略 (Multiplexing Strategy)¶
- 在创建新的会话层之前,客户端必须检查池中是否有“空闲”的会话。
- 如果有,客户端必须选取序号 (
Seq) 最大的会话来开启新流。 - 如果没有空闲会话,则创建新会话。
Seq在客户端实例内必须单调递增。 - 当流正常关闭且会话无错误时,会话将放入“空闲会话池”,并设置其空闲起始时间为
now。 - 客户端应该定期(例如 30 秒)清理持续空闲超过一定时间(例如 60 秒)的会话。
- 服务端也可以定期清理长期无上下行数据的会话。
8. 代理协议交互 (Proxy Protocol Interaction)¶
8.1. TCP 代理¶
流打开后(cmdSYN),客户端必须在 cmdPSH 中发送 RFC 1928 (SOCKS5) 格式的目标地址。随后开始双向代理中继。
8.2. UDP 代理¶
UDP 代理依赖于 sing-box udp-over-tcp v2 协议。客户端将其视作对特殊域名 sp.v2.udp-over-tcp.arpa 的 TCP 代理请求。
9. 协议参数 (Protocol Parameters)¶
Anytls 协议参数不包括 TLS 配置。Anytls 特定参数包括:
9.1. 客户端参数¶
password(String, REQUIRED): 协议认证的密码。idleSessionCheckInterval(Duration, OPTIONAL): 检查空闲会话池的间隔时间。idleSessionTimeout(Duration, OPTIONAL): 会话在被关闭前可以保持空闲的最大时长。minIdleSession(Integer, OPTIONAL): 作为热备保留的最小空闲会话数量。
9.2. 服务端参数¶
paddingScheme(String, OPTIONAL): 强制下发给客户端的主填充方案。
10. 更新记录 (Version History)¶
- v1: 初始实现。
- v2 (v0.0.8 - 2025 年 4 月): 添加了
cmdSYNACK以报告出站连接状态并处理卡住的隧道;添加了cmdHeartRequest/cmdHeartResponse心跳包;添加了cmdServerSettings用于协商。 - v2 (v0.0.10 - 2025 年 9 月): 明确了关于会话和流关闭时
cmdFIN的语义。