Skip to content

TUIC 协议

MasakiMu319 ·

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 连接存活

ConnectPacket 命令会携带有效载荷(流数据 / 分片数据)。

Command Type Specific Data

Authenticate

+------+-------+
| UUID | TOKEN |
+------+-------+
|  16  |  32   |
+------+-------+

其中:

  • UUID - 客户端 UUID
  • TOKEN - 客户端令牌。客户端原始密码会基于当前 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 relaying
  • PKT_ID - UDP 数据包 ID。见 UDP relaying
  • FRAG_TOTAL - 该 UDP 数据包的总分片数
  • FRAG_ID - 当前分片在该 UDP 数据包中的分片 ID
  • SIZE - 该(分片)UDP 数据包长度
  • ADDR - 地址字段(客户端发来时表示目标地址,服务端发来时表示源地址)。见 Address

Dissociate

+----------+
| ASSOC_ID |
+----------+
|    2     |
+----------+

其中:

Heartbeat

+-+
| |
+-+
| |
+-+

Address

Address 是一个用于编码网络地址的变长字段。

+------+----------+----------+
| TYPE |   ADDR   |   PORT   |
+------+----------+----------+
|  1   | Variable |    2     |
+------+----------+----------+

其中:

  • TYPE - 地址类型
  • ADDR - 地址本体
  • PORT - 端口号

地址类型可能是:

  • 0xff: None
  • 0x00: 完全限定域名(首字节表示域名长度)
  • 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_IDFRAG_TOTALFRAG_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

  1. 性能突出

    • TCP:客户端发送 Connect 后可立即开始传输。
    • UDPASSOC_ID 设计支持 0-RTT 全锥形转发。
    • 效率:心跳与 native UDP 模式基于 QUIC datagram,可降低额外开销。
  2. 安全性强

  3. 设计简洁

    • 协议职责聚焦、边界清晰;
    • 命令帧易于解析;
    • 避免重复实现传输层职责。

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,仍需仔细设计状态生命周期、资源治理和明确的失败语义,补齐规范未完全覆盖的工程空白。