Files
ReTeamSpeak ea08823c97
CI/CD / Build Frontend (push) Failing after 11s
CI/CD / Test (macos-latest) (push) Has been cancelled
CI/CD / Test (windows-latest) (push) Has been cancelled
CI/CD / Build Desktop (linux) (push) Has been cancelled
CI/CD / Build Desktop (macos) (push) Has been cancelled
CI/CD / Build Desktop (windows) (push) Has been cancelled
CI/CD / Release (push) Has been cancelled
CI/CD / Test (ubuntu-latest) (push) Failing after 2s
Initial commit: ReTeamSpeak cross-platform TeamSpeak client
- tscore: Protocol implementation (packets, crypto, connection handshake)
- tsaudio: Audio engine (capture, playback, codec, VAD, jitter buffer)
- tsdb: SQLite database (identities, bookmarks, messages, settings)
- shared: Core types and events
- tauri-app: Tauri v2 desktop application with React frontend
- docs: SRS, SAD, SDD documentation
- CI/CD: GitHub Actions workflow
- 32 unit tests passing
2026-05-12 14:41:54 +09:00

34 KiB
Raw Permalink Blame History

TeamSpeak 3 协议栈分析

1. 协议栈层次结构

┌─────────────────────────────────────────────────────────────┐
│                    应用层 (Application Layer)                 │
│  - 命令/通知 (Commands/Notifications)                        │
│  - 文本消息、频道管理、客户端管理                             │
├─────────────────────────────────────────────────────────────┤
│                    消息层 (Message Layer)                     │
│  - 命令解析 (Command Parsing)                                │
│  - 参数编码/解码 (Parameter Encoding/Decoding)               │
│  - BBCode 格式                                               │
├─────────────────────────────────────────────────────────────┤
│                    数据包层 (Packet Layer)                    │
│  - 数据包格式 (Packet Format)                                │
│  - 数据包类型 (Packet Types)                                 │
│  - 分片/重组 (Fragmentation/Reassembly)                      │
│  - 压缩/解压 (Compression/Decompression)                     │
├─────────────────────────────────────────────────────────────┤
│                    加密层 (Encryption Layer)                  │
│  - AES-128-EAX 加密/解密                                     │
│  - ECDH 密钥交换                                             │
│  - 密钥派生 (Key Derivation)                                 │
├─────────────────────────────────────────────────────────────┤
│                    可靠传输层 (Reliable Transport Layer)      │
│  - 选择性重传 (Selective Repeat)                             │
│  - 拥塞控制 (Congestion Control)                             │
│  - 流量控制 (Flow Control)                                   │
├─────────────────────────────────────────────────────────────┤
│                    传输层 (Transport Layer)                   │
│  - UDP 协议                                                  │
│  - 最大包大小: 500 字节                                       │
└─────────────────────────────────────────────────────────────┘

2. 数据包格式详解

2.1 数据包头结构

客户端 → 服务器 (C2S)

 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        MAC (8 bytes)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Packet ID            |         Client ID             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PT  |                     Data (≤487 bytes)                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

服务器 → 客户端 (S2C)

 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        MAC (8 bytes)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Packet ID            | PT  |     Data (≤489 bytes)   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

字段说明:

字段 大小 类型 说明
MAC 8 字节 [u8; 8] EAX 消息认证码
PId 2 字节 u16 数据包 ID (网络字节序)
CId 2 字节 u16 客户端 ID (仅 C2S)
PT 1 字节 u8 数据包类型 + 标志位
Data 变长 [u8] 数据包负载

2.2 数据包类型和标志位

PT 字段结构

 7   6   5   4   3   2   1   0
+---+---+---+---+---+---+---+---+
|UE |CP |NP |FR |    Type     |
+---+---+---+---+---+---+---+---+
标志位 十六进制 说明
UE 7 0x80 未加密 (Unencrypted)
CP 6 0x40 已压缩 (Compressed)
NP 5 0x20 新协议 (Newprotocol)
FR 4 0x10 已分片 (Fragmented)
Type 3-0 0x00-0x08 数据包类型

数据包类型

类型 说明 方向
Voice 0x00 语音数据 双向
VoiceWhisper 0x01 私语数据 双向
Command 0x02 命令数据 双向
CommandLow 0x03 低优先级命令 双向
Ping 0x04 Ping 包 C2S
Pong 0x05 Pong 包 S2C
Ack 0x06 确认包 双向
AckLow 0x07 低优先级确认 双向
Init1 0x08 初始化包 双向

2.3 数据包类型特性

类型 需要确认 可重传 可加密 可分片 可压缩
Voice 可选
VoiceWhisper 可选
Command ✓ (Ack)
CommandLow ✓ (AckLow)
Ping ✓ (Pong)
Pong
Ack
AckLow
Init1 ✓ (Init1)

3. 数据包处理流程

3.1 发送流程

应用数据
    ↓
┌─────────────────────────────────────┐
│ 1. 命令序列化                        │
│    - 参数编码 (key=value)            │
│    - UTF-8 编码                      │
│    - BBCode 格式化                   │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 2. 压缩 (QuickLZ Level 1)           │
│    - 仅 Command/CommandLow          │
│    - 仅当压缩后更小时                │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 3. 分片                              │
│    - 最大 500 字节 (含头)            │
│    - 仅 Command/CommandLow          │
│    - 首尾包设置 FR 标志              │
│    - 首包设置 CP 标志                │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 4. 加密 (AES-128-EAX)              │
│    - 生成密钥和 Nonce               │
│    - EAX 模式加密                    │
│    - 生成 MAC                        │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 5. 分配 Packet ID                   │
│    - 每种类型独立计数器              │
│    - 溢出时增加 Generation ID        │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 6. 发送                              │
│    - UDP 发送                        │
│    - 加入重传队列 (如需要)           │
└─────────────────────────────────────┘

3.2 接收流程

UDP 数据包
    ↓
┌─────────────────────────────────────┐
│ 1. 验证                              │
│    - 检查地址                        │
│    - 检查客户端 ID                   │
│    - 检查接收窗口                    │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 2. 解密 (AES-128-EAX)              │
│    - 生成密钥和 Nonce               │
│    - EAX 模式解密                    │
│    - 验证 MAC                        │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 3. 重组                              │
│    - 处理分片队列                    │
│    - 按 Packet ID 排序              │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 4. 解压 (QuickLZ)                   │
│    - 仅当 CP 标志设置时             │
│    - 最大解压大小: 2MB              │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 5. 命令解析                          │
│    - UTF-8 解码                      │
│    - 参数解析 (key=value)           │
│    - 转义序列处理                    │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 6. 发送确认                          │
│    - Command → Ack                  │
│    - CommandLow → AckLow            │
│    - Ping → Pong                    │
└─────────────────────────────────────┘

4. 加密机制详解

4.1 EAX 模式加密

TeamSpeak 3 使用 AES-128-EAX 模式进行加密,EAX 模式是一种认证加密模式,提供:

  • 机密性: AES-128-CTR 加密
  • 完整性: OMAC (One-key MAC) 消息认证
  • 真实性: 8 字节 MAC 标签

4.2 密钥派生

密钥生成算法

fn create_key_nonce(
    p_type: PacketType,     // 数据包类型
    c_id: Option<u16>,      // 客户端 ID (方向)
    p_id: u16,              // 数据包 ID
    generation_id: u32,     // 代 ID
    iv: &[u8; 64],          // 共享 IV
) -> (Key, Nonce) {
    // 构建临时数组
    let mut temp = [0; 70];
    temp[0] = if c_id.is_some() { 0x31 } else { 0x30 };  // 方向
    temp[1] = p_type as u8;                               // 类型
    temp[2..6] = generation_id.to_be_bytes();             // 代 ID
    temp[6..] = iv;                                       // 共享 IV
    
    // SHA-256 哈希
    let keynonce = sha256(&temp);
    
    // 提取密钥和 Nonce
    let key = keynonce[0..16];
    let nonce = keynonce[16..32];
    
    // 用 Packet ID 修改密钥
    key[0] ^= (p_id >> 8) as u8;
    key[1] ^= (p_id & 0xff) as u8;
    
    (key, nonce)
}

密钥缓存

为提高性能,可以缓存每个 (PacketType, Direction, GenerationId) 组合的密钥:

  • 缓存大小: 8 种类型 × 2 种方向 = 16 个条目
  • 仅在 Generation ID 变化时重新计算

4.3 假加密 (Fake Encryption)

某些数据包使用固定的假密钥进行加密:

const FAKE_KEY: [u8; 16] = *b"c:\\windows\\syste";
const FAKE_NONCE: [u8; 16] = *b"m\\firewall32.cpl";

使用假加密的场景:

  1. 初始化期间的命令包
  2. 客户端的第一个 Ack 包
  3. 服务器的第一个命令包

4.4 共享密钥计算

旧协议 (<3.1)

fn compute_shared_iv_mac_old(
    alpha: &[u8; 10],           // 客户端随机数
    beta: &[u8; 10],            // 服务器随机数
    server_pubkey: &PublicKey,  // 服务器公钥
    client_privkey: &PrivateKey, // 客户端私钥
) -> (SharedIV, SharedMac) {
    // ECDH 密钥交换
    let shared_secret = ecdh(server_pubkey, client_privkey);
    let x = shared_secret.x.to_bytes();
    
    // 填充到 32 字节
    let mut shared_data = [0; 32];
    shared_data[32 - x.len()..].copy_from_slice(&x);
    
    // SHA-1 哈希
    let mut shared_iv = sha1(&shared_data);
    
    // 与 alpha/beta 异或
    shared_iv[0..10] ^= alpha;
    shared_iv[10..20] ^= beta;
    
    // 计算 MAC
    let shared_mac = sha1(&shared_iv)[0..8];
    
    (shared_iv, shared_mac)
}

新协议 (≥3.1)

fn compute_shared_iv_mac_new(
    alpha: &[u8; 10],           // 客户端随机数
    beta: &[u8; 54],            // 服务器随机数 (更长)
    licenses: &Licenses,        // 许可证链
    client_privkey: &Ed25519PrivateKey, // 客户端临时私钥
) -> (SharedIV, SharedMac) {
    // 从许可证链派生服务器公钥
    let server_pubkey = derive_key_from_licenses(licenses);
    
    // Curve25519 密钥交换
    let shared_secret = client_privkey.create_shared_secret(&server_pubkey);
    
    // SHA-512 哈希
    let mut shared_iv = sha512(&shared_secret);
    
    // 与 alpha/beta 异或
    shared_iv[0..10] ^= alpha;
    shared_iv[10..64] ^= beta;
    
    // 计算 MAC
    let shared_mac = sha1(&shared_iv)[0..8];
    
    (shared_iv, shared_mac)
}

5. 可靠传输机制

5.1 数据包确认

确认机制

  • Command 包需要 Ack 确认
  • CommandLow 包需要 AckLow 确认
  • Ping 包需要 Pong 确认
  • Init1 包需要下一个 Init1 确认

确认包格式

+--+--+
| PId |
+--+--+
  • PId: 被确认的数据包 ID (2 字节)

5.2 选择性重传

struct Resender {
    // 发送队列: 等待确认的数据包
    send_queue: BTreeMap<PacketId, SentPacket>,
    
    // 重传超时计算
    rtt_estimate: Duration,
    rtt_deviation: Duration,
    
    // 拥塞窗口
    congestion_window: u32,
}

struct SentPacket {
    data: Vec<u8>,
    send_time: Instant,
    retry_count: u32,
}

重传策略

  1. 超时计算: 使用指数退避算法

    timeout = rtt_estimate + 4 * rtt_deviation
    timeout = min(timeout * 2^retry_count, MAX_TIMEOUT)
    
  2. 重传条件:

    • 数据包超时未确认
    • 收到重复的确认包
  3. 放弃条件:

    • 超过 30 秒未确认
    • 达到最大重试次数

5.3 拥塞控制

TeamSpeak 3 使用类似 TCP CUBIC 的拥塞控制算法:

struct CongestionControl {
    // 拥塞窗口大小 (数据包数量)
    cwnd: u32,
    
    // 慢启动阈值
    ssthresh: u32,
    
    // 丢包检测
    loss_detector: LossDetector,
}

impl CongestionControl {
    fn on_ack(&mut self, acked_bytes: u32) {
        if self.cwnd < self.ssthresh {
            // 慢启动阶段: 指数增长
            self.cwnd += acked_bytes;
        } else {
            // 拥塞避免阶段: 线性增长
            self.cwnd += acked_bytes / self.cwnd;
        }
    }
    
    fn on_loss(&mut self) {
        // 丢包检测: 减小拥塞窗口
        self.ssthresh = self.cwnd / 2;
        self.cwnd = self.ssthresh;
    }
}

5.4 流量控制

接收窗口

struct ReceiveWindow {
    // 下一个期望的数据包 ID
    next_expected: u16,
    
    // 接收队列大小
    queue_size: u16,
    
    // 最大队列长度
    max_queue_len: u16,  // 200
}

发送窗口

struct SendWindow {
    // 当前发送的数据包 ID
    current_id: u16,
    
    // 拥塞窗口大小
    congestion_window: u32,
    
    // 已发送未确认的数据包数量
    in_flight: u32,
}

6. 命令协议详解

6.1 命令格式

命令使用类似 TeamSpeak 3 Query 的文本格式,UTF-8 编码:

command_name key1=value1 key2=value2 ...

转义序列

原始字符 转义序列 说明
\ \\ 反斜杠
\s 空格
` ` \p
/ \/ 斜杠
\n \n 换行符

6.2 命令类型

请求-响应命令

客户端发送:
  clientinit client_nickname=MyName client_version=3.0.19.3 ...

服务器响应:
  initserver server_name=MyServer server_platform=Linux ...

通知命令

服务器主动推送:
  notifycliententerview cfid=1 ctid=0 reasonid=0 clid=1 client_database_id=1 ...

列表命令

客户端请求:
  channellist -topic -flags -limits

服务器响应 (多行):
  cid=1 cpid=0 channel_name=Default\sChannel ...
  cid=2 cpid=1 channel_name=Sub\sChannel ...
  channellistfinished

6.3 关键命令

连接初始化

// 客户端初始化
clientinit {
    client_nickname: str,           // 昵称
    client_version: str,            // 版本
    client_platform: str,           // 平台
    client_input_hardware: bool,    // 输入设备
    client_output_hardware: bool,   // 输出设备
    client_default_channel: str,    // 默认频道
    client_default_channel_password: str, // 频道密码
    client_server_password: str,    // 服务器密码
    client_meta_data: str,          // 元数据
    client_version_sign: str,       // 版本签名
    client_key_offset: u64,         // Hash Cash 偏移
    client_nickname_phonetic: str,  // 语音昵称
    client_default_token: str,      // 权限令牌
    hwid: str,                      // 硬件 ID
}

// 服务器初始化响应
initserver {
    server_name: str,               // 服务器名称
    server_platform: str,           // 平台
    server_version: str,            // 版本
    server_maxclients: u16,         // 最大客户端数
    // ... 更多参数
}

频道操作

// 创建频道
channelcreate {
    channel_name: str,              // 频道名称
    channel_topic: str,             // 主题
    channel_codec: Codec,           // 编解码器
    channel_codec_quality: u8,      // 质量
    channel_maxclients: i32,        // 最大客户端数
    channel_flag_permanent: bool,   // 永久频道
    channel_password: str,          // 密码
    // ... 更多参数
}

// 频道列表
channellist {
    cid: ChannelId,                 // 频道 ID
    cpid: ChannelId,                // 父频道 ID
    channel_name: str,              // 频道名称
    channel_topic: str,             // 主题
    channel_codec: Codec,           // 编解码器
    channel_maxclients: i32,        // 最大客户端数
    channel_flag_permanent: bool,   // 永久频道
    // ... 更多参数
}

客户端操作

// 发送消息
sendtextmessage {
    targetmode: TextMessageTargetMode, // 目标模式
    target: ClientId,                  // 目标客户端
    msg: str,                          // 消息内容
}

// 客户端进入视图
notifycliententerview {
    cfid: ChannelId,                   // 来源频道
    ctid: ChannelId,                   // 目标频道
    reasonid: Reason,                  // 原因
    clid: ClientId,                    // 客户端 ID
    client_database_id: ClientDbId,    // 数据库 ID
    client_nickname: str,              // 昵称
    client_unique_identifier: Uid,     // 唯一标识符
    client_type: ClientType,           // 客户端类型
    // ... 更多参数
}

7. 语音传输协议

7.1 语音包格式

客户端 → 服务器 (C2S)

+--+--+--+---------//---------+
| VId |C |        Data        |
+--+--+--+---------//---------+
字段 类型 说明
VId u16 语音包 ID
C u8 编解码器类型
Data [u8] 语音数据

服务器 → 客户端 (S2C)

+--+--+--+--+--+---------//---------+
| VId | CId |C |        Data        |
+--+--+--+--+--+---------//---------+
字段 类型 说明
VId u16 语音包 ID
CId u16 说话客户端 ID
C u8 编解码器类型
Data [u8] 语音数据

7.2 私语包格式

直接目标模式 (NP=0)

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+---------//---------+
| VId |C |N |M |           U*          |  T* |        Data        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+---------//---------+
字段 类型 说明
VId u16 语音包 ID
C u8 编解码器类型
N u8 目标频道数
M u8 目标客户端数
U [u64] 目标频道 ID (N 个)
T [u16] 目标客户端 ID (M 个)
Data [u8] 语音数据

组目标模式 (NP=1)

+--+--+--+--+--+--+--+--+--+--+--+--+--+---------//---------+
| VId |C |TY|TA|           U           |        Data        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+---------//---------+
字段 类型 说明
VId u16 语音包 ID
C u8 编解码器类型
TY u8 组私语类型
TA u8 组私语目标
U u64 目标频道/组 ID
Data [u8] 语音数据

组私语类型

enum GroupWhisperType : u8 {
    ServerGroup = 0,      // 服务器组
    ChannelGroup = 1,     // 频道组
    ChannelCommander = 2, // 频道指挥官
    AllClients = 3,       // 所有客户端
}

enum GroupWhisperTarget : u8 {
    AllChannels = 0,           // 所有频道
    CurrentChannel = 1,        // 当前频道
    ParentChannel = 2,         // 父频道
    AllParentChannel = 3,      // 所有父频道
    ChannelFamily = 4,         // 频道家族
    CompleteChannelFamily = 5, // 完整频道家族
    Subchannels = 6,           // 子频道
}

7.3 编解码器

编解码器 采样率 声道 说明
SpeexNarrowband 0 8 kHz 单声道 窄带语音
SpeexWideband 1 16 kHz 单声道 宽带语音
SpeexUltrawideband 2 32 kHz 单声道 超宽带语音
CeltMono 3 48 kHz 单声道 CELT 编码
OpusVoice 4 48 kHz 单声道 Opus 语音优化
OpusMusic 5 48 kHz 立体声 Opus 音乐优化

7.4 语音流处理

struct VoiceStream {
    // 语音包 ID
    packet_id: u16,
    
    // 编解码器
    codec: Codec,
    
    // 解码器
    decoder: OpusDecoder,
    
    // 抖动缓冲
    jitter_buffer: JitterBuffer,
    
    // 丢包隐藏
    packet_loss_concealment: PlcState,
}

impl VoiceStream {
    fn process_packet(&mut self, packet: &InAudio) -> Vec<f32> {
        // 1. 检查丢包
        let lost_packets = self.check_packet_loss(packet.id);
        
        // 2. 丢包隐藏
        if lost_packets > 0 {
            self.conceal_packet_loss(lost_packets);
        }
        
        // 3. 解码音频
        let samples = self.decoder.decode(&packet.data);
        
        // 4. 添加到抖动缓冲
        self.jitter_buffer.push(samples);
        
        // 5. 获取输出
        self.jitter_buffer.get()
    }
}

8. 初始化握手详解

8.1 低级握手 (Init1)

客户端                                      服务器
   |                                          |
   |──── Init0 (version, timestamp, A0) ────>|
   |                                          |
   |<─── Init1 (A1, A0r) ────────────────────|
   |                                          |
   |──── Init2 (version, A1, A0r) ──────────>|
   |                                          |
   |<─── Init3 (x, n, level, A2) ────────────|
   |                                          |
   |──── Init4 (version, x, n, level, A2,   |
   |           y, clientinitiv) ────────────>|
   |                                          |

Init0 (客户端 → 服务器)

struct Init0 {
    version: u32,        // 客户端版本时间戳
    step: u8,            // 步骤号: 0x00
    timestamp: u32,      // 当前时间戳
    random0: [u8; 4],    // 随机数 A0
    reserved: [u8; 8],   // 保留字节
}

Init1 (服务器 → 客户端)

struct Init1 {
    step: u8,            // 步骤号: 0x01
    random1: [u8; 16],   // 服务器随机数 A1
    random0_r: [u8; 4],  // A0 反转 (通常)
}

Init3 (服务器 → 客户端)

struct Init3 {
    step: u8,            // 步骤号: 0x03
    x: [u8; 64],         // RSA 底数 x
    n: [u8; 64],         // RSA 模数 n
    level: u32,          // 难度级别
    random2: [u8; 100],  // 服务器随机数 A2
}

Init4 (客户端 → 服务器)

struct Init4 {
    version: u32,        // 客户端版本时间戳
    step: u8,            // 步骤号: 0x04
    x: [u8; 64],         // 接收的 x
    n: [u8; 64],         // 接收的 n
    level: u32,          // 接收的 level
    random2: [u8; 100],  // 接收的 A2
    y: [u8; 64],         // y = x^(2^level) mod n
    command: Command,    // clientinitiv 命令
}

8.2 高级握手

clientinitiv (客户端 → 服务器)

clientinitiv alpha={alpha} omega={omega} ot={ot} ip={ip}
  • alpha: 10 随机字节的 base64 编码
  • omega: ASN.1-DER 编码的 ECDH 公钥
  • ot: 固定为 1
  • ip: 服务器 IP 地址

initivexpand/initivexpand2 (服务器 → 客户端)

旧协议 (<3.1):

initivexpand alpha={alpha} beta={beta} omega={omega}

新协议 (≥3.1):

initivexpand2 l={l} beta={beta} omega={omega} ot={ot} proof={proof} tvd={tvd}

clientek (客户端 → 服务器)

clientek ek={ek} proof={proof}
  • ek: 客户端临时公钥 (base64)
  • proof: 签名的客户端公钥 + beta

8.3 许可证验证 (新协议)

struct License {
    version: u8,                    // 版本: 0x01
    blocks: Vec<LicenseBlock>,      // 许可证块
}

struct LicenseBlock {
    key_type: u8,                   // 密钥类型: 0x00
    public_key: [u8; 32],           // 块公钥
    block_type: LicenseBlockType,   // 块类型
    not_before: u32,                // 有效期开始
    not_after: u32,                 // 有效期结束
    content: Vec<u8>,               // 内容
}

enum LicenseBlockType {
    Intermediate = 0x00,    // 中间证书
    Website = 0x01,         // 网站证书
    Code = 0x03,            // 代码证书
    Server = 0x02,          // 服务器证书
    TS5Server = 0x08,       // TS5 服务器证书
    Ephemeral = 0x32,       // 临时证书
}

9. 数据包 ID 管理

9.1 数据包 ID 计数器

每种数据包类型和方向有独立的 ID 计数器:

struct PacketIdManager {
    // 出站计数器 (客户端有 9 个)
    outgoing: [PartialPacketId; 9],
    
    // 入站计数器
    incoming: [PartialPacketId; 9],
}

struct PartialPacketId {
    generation_id: u32,  // 代 ID
    packet_id: u16,      // 数据包 ID
}

9.2 代 ID (Generation ID)

数据包 ID 存储为 u16,范围 0-65535

  • 当 ID 从 65535 溢出到 0 时,代 ID 增加 1
  • 代 ID 仅用于加密参数生成
  • 新的代 ID 立即应用于溢出的数据包

9.3 接收窗口

struct ReceiveWindow {
    next_expected: u16,     // 下一个期望的 ID
    max_queue_len: u16,     // 最大队列长度 (200)
    queue: Vec<Vec<u8>>,    // 乱序数据包队列
}

impl ReceiveWindow {
    fn in_window(&self, id: u16) -> bool {
        let diff = id.wrapping_sub(self.next_expected);
        diff < self.max_queue_len
    }
}

10. 心跳机制

10.1 Ping/Pong

客户端                                      服务器
   |                                          |
   |──── Ping (空) ─────────────────────────>|
   |                                          |
   |<─── Pong (PId) ─────────────────────────|
   |                                          |
   |<─── Ping (空) ──────────────────────────|
   |                                          |
   |──── Pong (PId) ────────────────────────>|

10.2 心跳配置

struct HeartbeatConfig {
    // 客户端 Ping 间隔
    client_ping_interval: Duration,  // 通常 30 秒
    
    // 服务器 Ping 间隔
    server_ping_interval: Duration,  // 通常 60 秒
    
    // 超时时间
    timeout: Duration,               // 通常 120 秒
}

11. 错误处理

11.1 错误码

enum Ts3ErrorCode {
    // 成功
    Ok = 0x0000,
    
    // 客户端错误 (0x0200-0x020F)
    ClientInvalidId = 0x0200,
    ClientNicknameInuse = 0x0201,
    ClientInvalidPassword = 0x0208,
    
    // 频道错误 (0x0300-0x030F)
    ChannelInvalidId = 0x0300,
    ChannelNameInuse = 0x0303,
    
    // 服务器错误 (0x0400-0x040D)
    ServerInvalidId = 0x0400,
    ServerMaxclientsReached = 0x0403,
    
    // 数据库错误 (0x0500-0x0505)
    Database = 0x0500,
    
    // 参数错误 (0x0600-0x0607)
    ParameterQuote = 0x0600,
    ParameterInvalidCount = 0x0601,
    
    // 连接错误 (0x0700-0x070C)
    ConnectionLost = 0x0701,
    NotConnected = 0x0702,
}

11.2 错误响应格式

error id=0 msg=ok
error id=256 msg=invalid\sclientID

12. 性能优化

12.1 密钥缓存

struct KeyCache {
    // 缓存每个 (类型, 方向, 代ID) 的密钥
    cache: [[CachedKey; 2]; 8],
}

struct CachedKey {
    generation_id: u32,
    key: [u8; 16],
    nonce: [u8; 16],
}

12.2 数据包批处理

struct PacketBatch {
    // 批量发送的数据包
    packets: Vec<OutPacket>,
    
    // 批量大小
    batch_size: usize,
    
    // 批量超时
    batch_timeout: Duration,
}

12.3 音频优化

struct AudioOptimization {
    // 抖动缓冲大小
    jitter_buffer_size: usize,
    
    // 丢包隐藏
    packet_loss_concealment: bool,
    
    // 前向纠错 (FEC)
    forward_error_correction: bool,
    
    // 速度调整
    speed_adjustment: bool,
}

13. 安全考虑

13.1 防 DoS 攻击

RSA 拼图机制:

fn solve_rsa_puzzle(x: &[u8; 64], n: &[u8; 64], level: u32) -> [u8; 64] {
    let x = BigUint::from_bytes_be(x);
    let n = BigUint::from_bytes_be(n);
    
    // y = x^(2^level) mod n
    let mut y = x.clone();
    for _ in 0..(1 << level) {
        y = y.pow(2) % &n;
    }
    
    biguint_to_array(&y)
}

13.2 Hash Cash

防止身份伪造:

fn get_hash_cash_level(omega: &str, offset: u64) -> u8 {
    let data = sha1(format!("{}{}", omega, offset));
    let mut level = 0;
    
    for byte in data {
        if byte == 0 {
            level += 8;
        } else {
            level += byte.trailing_zeros() as u8;
            break;
        }
    }
    
    level
}

13.3 身份验证

fn verify_identity(
    public_key: &PublicKey,
    signature: &[u8],
    data: &[u8],
) -> bool {
    // 使用 P-256 曲线验证签名
    public_key.verify(signature, data)
}

14. 协议版本兼容性

14.1 版本检测

fn detect_protocol_version(server_response: &str) -> ProtocolVersion {
    if server_response.contains("initivexpand2") {
        ProtocolVersion::New  // ≥3.1
    } else {
        ProtocolVersion::Old  // <3.1
    }
}

14.2 兼容性处理

struct ProtocolHandler {
    version: ProtocolVersion,
    
    // 根据版本选择不同的处理逻辑
    fn handle_init(&self, packet: &InPacket) -> Result<()> {
        match self.version {
            ProtocolVersion::Old => self.handle_init_old(packet),
            ProtocolVersion::New => self.handle_init_new(packet),
        }
    }
}

15. 实现参考

15.1 Rust 实现 (tsproto)

// 核心类型
pub struct Connection {
    pub is_client: bool,
    pub params: Option<ConnectedParams>,
    pub address: SocketAddr,
    pub resender: Resender,
    pub codec: PacketCodec,
    pub udp_socket: Box<dyn Socket + Send>,
}

// 加密参数
pub struct ConnectedParams {
    pub shared_iv: [u8; 64],
    pub shared_mac: [u8; 8],
    pub key_cache: [[CachedKey; 2]; 8],
    pub c_id: u16,
    pub voice_encryption: bool,
}

// 数据包编解码器
pub struct PacketCodec {
    pub outgoing_p_ids: [PartialPacketId; 8],
    pub incoming_p_ids: [PartialPacketId; 8],
    pub receive_queue: [Vec<Vec<u8>>; 2],
    pub fragmented_queue: [Option<Vec<u8>>; 2],
}

15.2 关键算法

// 压缩和分片
pub fn compress_and_split(is_client: bool, packet: OutPacket) -> Vec<OutPacket> {
    // 1. 检查是否需要压缩
    // 2. QuickLZ 压缩
    // 3. 分片 (最大 500 字节)
    // 4. 设置标志位
}

// 加密
pub fn encrypt(
    packet: &mut OutPacket,
    generation_id: u32,
    iv: &[u8; 64],
    cache: &mut [[CachedKey; 2]; 8],
) -> Result<()> {
    // 1. 生成密钥和 Nonce
    // 2. AES-128-EAX 加密
    // 3. 生成 MAC
}

// 解密
pub fn decrypt(
    packet: &InPacket,
    generation_id: u32,
    iv: &[u8; 64],
    cache: &mut [[CachedKey; 2]; 8],
) -> Result<Vec<u8>> {
    // 1. 生成密钥和 Nonce
    // 2. AES-128-EAX 解密
    // 3. 验证 MAC
}