Version
0x05
Overview
TUIC 协议依赖一个可多路复用的 TLS 加密流。所有转发任务都通过 Command 中的 Header 进行协商。
该协议不关心底层传输实现,但主要面向 QUIC 设计。详细机制见 Protocol Flow。
除非特别说明,所有字段均为大端序(Big Endian)。
Command
+-----+------+----------+
| VER | TYPE | OPT |
+-----+------+----------+
| 1 | 1 | Variable |
+-----+------+----------+
其中:
VER- TUIC 协议版本TYPE- 命令类型OPT- 该命令类型对应的特定数据
Command Types
共有五种命令类型:
0x00-Authenticate- 用于认证多路复用流0x01-Connect- 用于建立 TCP 转发0x02-Packet- 用于转发 UDP 数据包(或其分片)0x03-Dissociate- 用于终止一个 UDP 转发会话0x04-Heartbeat- 用于保持 QUIC 连接存活
Connect 与 Packet 命令会携带有效载荷(流数据 / 分片数据)。
Command Type Specific Data
Authenticate
+------+-------+
| UUID | TOKEN |
+------+-------+
| 16 | 32 |
+------+-------+
其中:
UUID- 客户端 UUIDTOKEN- 客户端令牌。客户端原始密码会基于当前 TLS 会话,使用 TLS Keying Material Exporter 哈希为一个 256 位令牌。导出时,label应为客户端 UUID,context应为原始密码。
Connect
+----------+
| ADDR |
+----------+
| Variable |
+----------+
其中:
ADDR- 目标地址。见 Address
Packet
+----------+--------+------------+---------+------+----------+
| ASSOC_ID | PKT_ID | FRAG_TOTAL | FRAG_ID | SIZE | ADDR |
+----------+--------+------------+---------+------+----------+
| 2 | 2 | 1 | 1 | 2 | Variable |
+----------+--------+------------+---------+------+----------+
其中:
ASSOC_ID- UDP 转发会话 ID。见 UDP relayingPKT_ID- UDP 数据包 ID。见 UDP relayingFRAG_TOTAL- 该 UDP 数据包的总分片数FRAG_ID- 当前分片在该 UDP 数据包中的分片 IDSIZE- 该(分片)UDP 数据包长度ADDR- 地址字段(客户端发来时表示目标地址,服务端发来时表示源地址)。见 Address
Dissociate
+----------+
| ASSOC_ID |
+----------+
| 2 |
+----------+
其中:
ASSOC_ID- UDP 转发会话 ID。见 UDP relaying
Heartbeat
+-+
| |
+-+
| |
+-+
Address
Address 是一个用于编码网络地址的变长字段。
+------+----------+----------+
| TYPE | ADDR | PORT |
+------+----------+----------+
| 1 | Variable | 2 |
+------+----------+----------+
其中:
TYPE- 地址类型ADDR- 地址本体PORT- 端口号
地址类型可能是:
0xff: None0x00: 完全限定域名(首字节表示域名长度)0x01: IPv4 地址0x02: IPv6 地址
None 地址类型用于 Packet 命令中“非首个分片”的场景。
端口号位于域名 / IP 地址之后,占 2 字节。
Protocol Flow
本节以 QUIC 作为底层传输,说明协议流程。
协议本身不强制底层传输管理方式。它甚至可以集成到已有服务中,例如 HTTP/3。
下面是 TUIC 在 QUIC 连接上的典型流程:
Authentication
客户端打开一个 unidirectional_stream,发送 Authenticate 命令。该过程可与其他命令并行。
服务端验证令牌。若有效,则连接完成认证,可继续处理转发任务。
若服务端在认证前收到了其他命令,应先解析命令头并暂停;认证完成后再恢复这些任务。
TCP relaying
Connect 用于初始化 TCP 转发。
客户端打开 bidirectional_stream 并发送 Connect。命令头发送完成后,客户端可立即开始转发,无需等待服务端响应。
服务端收到 Connect 后,向目标地址建立 TCP 连接,然后在 TCP 流与 QUIC 双向流之间转发数据。
UDP relaying
TUIC 通过在客户端和服务端之间同步 UDP 会话 ID(ASSOC_ID),实现 0-RTT 全锥形 UDP 转发。
双方应按每条 QUIC 连接维护 UDP 会话表,将每个 ASSOC_ID 映射到对应 UDP 套接字。
ASSOC_ID 由客户端生成,为 16 位无符号整数。若客户端希望服务端复用同一 UDP 套接字,应复用同一 ASSOC_ID。
服务端收到 Packet 后会检查 ASSOC_ID 是否已存在。若不存在,则为该关联分配 UDP 套接字。服务端使用该套接字向外发送客户端请求的 UDP 流量,也从任意目标接收回包,再封装 Packet 头返回客户端。
一个 UDP 数据包可拆分为多个 Packet 命令。PKT_ID、FRAG_TOTAL 与 FRAG_ID 用于识别和重组分片。
作为客户端,Packet 可以通过以下方式发送:
- QUIC
unidirectional_stream(UDP 转发模式quic) - QUIC
datagram(UDP 转发模式native)
服务端在某个关联首次收到 Packet 后,下行回包应使用相同模式。
客户端可通过 QUIC unidirectional_stream 发送 Dissociate,解除一个 UDP 会话。服务端随后删除会话并释放对应 UDP 套接字。
Heartbeat
只要有转发任务进行中,客户端应周期性通过 QUIC datagram 发送 Heartbeat,以保持连接存活。
Error Handling
协议未为任何命令定义标准响应。若服务端收到非法命令,或在处理时出现错误(例如目标不可达、认证失败),协议层没有统一处理规范。具体行为由实现决定:服务端可以关闭 QUIC 连接,也可以忽略该命令。
例如,当服务端收到目标不可达的 Connect 时,可通过关闭对应 bidirectional_stream 来表示失败。
In-depth Protocol Analysis
深入看,TUIC 是一种现代高性能设计,能有效利用 QUIC 与 TLS;但其极简规范也把大量实现责任交给了工程方。
Executive Summary
TUIC 是一个紧凑、安全、面向低延迟的代理协议。它在多路复用 TLS 流(理想情况下是 QUIC)上使用简洁命令结构,并将加密与可靠性复杂度下放到下层传输。
其亮点是高效的 0-RTT 全锥形 UDP 转发,适合实时业务。
核心权衡是 错误处理未标准化、由实现定义。这让规范保持简洁,但把稳健性、安全性与互操作性负担转移给开发者。
Key Strengths
-
性能突出
- TCP:客户端发送
Connect后可立即开始传输。 - UDP:
ASSOC_ID设计支持 0-RTT 全锥形转发。 - 效率:心跳与 native UDP 模式基于 QUIC datagram,可降低额外开销。
- TCP:客户端发送
-
安全性强
- 认证:通过 TLS Keying Material Exporter (RFC 5705) 将令牌与 TLS 会话绑定,有助于防重放。
- 加密:所有流量由底层 TLS 保护。
-
设计简洁
- 协议职责聚焦、边界清晰;
- 命令帧易于解析;
- 避免重复实现传输层职责。
Critical Implementation Challenges & Recommendations
1. UDP ASSOC_ID 生命周期管理(DoS 风险)
- 问题:规范定义了如何创建关联,但未给出清理策略。恶意客户端可无限创建关联,耗尽文件描述符与内存。
- 建议:为关联映射实现自动驱逐缓存策略。
- 使用 TTL 或 LRU 驱逐;
- 访问时刷新 TTL;
- 后台周期扫描并关闭/移除过期关联与套接字;
- 可采用 300 秒 左右的默认 TTL。
2. UDP 分片缓冲区管理(DoS 风险)
- 问题:攻击者可大量发送首分片(
FRAG_ID=0)但不补齐后续分片,导致内存无上限增长。 - 建议:对重组缓冲实行严格治理。
- 限制每个
ASSOC_ID的并发重组数量(例如 4-8); - 设置较短重组超时(例如 1-2 秒);
- 为全部分片缓冲设置全局内存上限作为最终防线。
- 限制每个
3. 错误处理标准化
- 问题:若缺乏明确错误语义,失败会变得隐式,且不同实现会分叉。
- 建议:定义显式、与传输层能力一致的错误策略。
- 认证失败:连接级致命错误,关闭 QUIC 连接并返回专用应用错误码;
- TCP 连接失败:流级错误,关闭对应双向流;
- 非法命令:关闭承载该非法命令的流。
Conclusion
TUIC 是一个实用且强健的协议,适合安全、低延迟的混合 TCP/UDP 隧道场景。但“规范简洁”不等于“生产实现简单”。要做出生产级 TUIC,仍需仔细设计状态生命周期、资源治理和明确的失败语义,补齐规范未完全覆盖的工程空白。