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

1166 lines
34 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 密钥派生
#### 密钥生成算法
```rust
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)
某些数据包使用固定的假密钥进行加密:
```rust
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)
```rust
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)
```rust
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 选择性重传
```rust
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 的拥塞控制算法:
```rust
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 流量控制
#### 接收窗口
```rust
struct ReceiveWindow {
// 下一个期望的数据包 ID
next_expected: u16,
// 接收队列大小
queue_size: u16,
// 最大队列长度
max_queue_len: u16, // 200
}
```
#### 发送窗口
```rust
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 关键命令
#### 连接初始化
```rust
// 客户端初始化
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, // 最大客户端数
// ... 更多参数
}
```
#### 频道操作
```rust
// 创建频道
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, // 永久频道
// ... 更多参数
}
```
#### 客户端操作
```rust
// 发送消息
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] | 语音数据 |
#### 组私语类型
```rust
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 语音流处理
```rust
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 (客户端 → 服务器)
```rust
struct Init0 {
version: u32, // 客户端版本时间戳
step: u8, // 步骤号: 0x00
timestamp: u32, // 当前时间戳
random0: [u8; 4], // 随机数 A0
reserved: [u8; 8], // 保留字节
}
```
#### Init1 (服务器 → 客户端)
```rust
struct Init1 {
step: u8, // 步骤号: 0x01
random1: [u8; 16], // 服务器随机数 A1
random0_r: [u8; 4], // A0 反转 (通常)
}
```
#### Init3 (服务器 → 客户端)
```rust
struct Init3 {
step: u8, // 步骤号: 0x03
x: [u8; 64], // RSA 底数 x
n: [u8; 64], // RSA 模数 n
level: u32, // 难度级别
random2: [u8; 100], // 服务器随机数 A2
}
```
#### Init4 (客户端 → 服务器)
```rust
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 许可证验证 (新协议)
```rust
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 计数器:
```rust
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 接收窗口
```rust
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 心跳配置
```rust
struct HeartbeatConfig {
// 客户端 Ping 间隔
client_ping_interval: Duration, // 通常 30 秒
// 服务器 Ping 间隔
server_ping_interval: Duration, // 通常 60 秒
// 超时时间
timeout: Duration, // 通常 120 秒
}
```
## 11. 错误处理
### 11.1 错误码
```rust
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 密钥缓存
```rust
struct KeyCache {
// 缓存每个 (类型, 方向, 代ID) 的密钥
cache: [[CachedKey; 2]; 8],
}
struct CachedKey {
generation_id: u32,
key: [u8; 16],
nonce: [u8; 16],
}
```
### 12.2 数据包批处理
```rust
struct PacketBatch {
// 批量发送的数据包
packets: Vec<OutPacket>,
// 批量大小
batch_size: usize,
// 批量超时
batch_timeout: Duration,
}
```
### 12.3 音频优化
```rust
struct AudioOptimization {
// 抖动缓冲大小
jitter_buffer_size: usize,
// 丢包隐藏
packet_loss_concealment: bool,
// 前向纠错 (FEC)
forward_error_correction: bool,
// 速度调整
speed_adjustment: bool,
}
```
## 13. 安全考虑
### 13.1 防 DoS 攻击
RSA 拼图机制:
```rust
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
防止身份伪造:
```rust
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 身份验证
```rust
fn verify_identity(
public_key: &PublicKey,
signature: &[u8],
data: &[u8],
) -> bool {
// 使用 P-256 曲线验证签名
public_key.verify(signature, data)
}
```
## 14. 协议版本兼容性
### 14.1 版本检测
```rust
fn detect_protocol_version(server_response: &str) -> ProtocolVersion {
if server_response.contains("initivexpand2") {
ProtocolVersion::New // ≥3.1
} else {
ProtocolVersion::Old // <3.1
}
}
```
### 14.2 兼容性处理
```rust
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)
```rust
// 核心类型
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 关键算法
```rust
// 压缩和分片
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
}
```