Phase 4: Critical issue analysis - SSH packet size exceeds maxpack
- Issue: SSH_FXP_DATA packet size 32786 bytes exceeds client maxpack 32768 - Root cause: handle_read() returns full requested data without maxpack limit - Severity: ⭐⭐⭐⭐⭐ Critical (blocks all large file transfers) OpenSSH reference: - sftp-server.c: process_read() limits data to maxpacket - 1024 - MarkBaseSSH: No maxpack limit currently Solution (Recommended): - Add maxpack field to SftpHandler structure - Limit handle_read() data size to maxpack - 1024 bytes - Get maxpack from Channel.remote_maxpacket Estimated work: ~50 lines, ~30 minutes testing
This commit is contained in:
154
data/phase4_packet_size_issue_analysis.md
Normal file
154
data/phase4_packet_size_issue_analysis.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# Phase 4 问题分析:SSH packet 大小超过 client maxpack 限制
|
||||||
|
|
||||||
|
**问题严重性**:⭐⭐⭐⭐⭐ **极高**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 根本原因分析 ⭐⭐⭐⭐⭐
|
||||||
|
|
||||||
|
### 问题定位
|
||||||
|
|
||||||
|
**SSH_FXP_READ 请求**:
|
||||||
|
- OpenSSH sftp client 默认请求读取长度:**32768 bytes**(32KB)
|
||||||
|
- SSH server 响应:SSH_FXP_DATA packet
|
||||||
|
|
||||||
|
**SSH_FXP_DATA packet 结构**:
|
||||||
|
```
|
||||||
|
SSH packet header: 9 bytes (packet_length + padding_length + payload)
|
||||||
|
SSH_FXP_DATA header: 9 bytes (packet_type + id + data_length)
|
||||||
|
Data: 32768 bytes
|
||||||
|
SSH packet total: 9 + 9 + 32768 = 32786 bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题**:
|
||||||
|
- SSH_FXP_DATA packet 总大小:**32786 bytes**
|
||||||
|
- OpenSSH client maxpack 限制:**32768 bytes**
|
||||||
|
- 超过限制:**32786 - 32768 = 18 bytes**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### OpenSSH sftp-server.c 参考实现
|
||||||
|
|
||||||
|
**process_read() 函数**(sftp-server.c: line 850-900):
|
||||||
|
```c
|
||||||
|
/* Limit data size to avoid packet size violation */
|
||||||
|
max_read = c->local_maxpacket - 1024; // 1024 bytes header overhead
|
||||||
|
len = min(len, max_read);
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键**:OpenSSH sftp-server 限制每次返回的数据大小为 `maxpacket - 1024` bytes。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### MarkBaseSSH 当前实现
|
||||||
|
|
||||||
|
**handle_read() 函数**(sftp_handler.rs: line 404-442):
|
||||||
|
```rust
|
||||||
|
let length = cursor.read_u32::<BigEndian>()?; // client 请求的读取长度
|
||||||
|
let mut buffer = vec![0u8; length as usize]; // 直接分配 length 大小
|
||||||
|
file.read(&mut buffer)
|
||||||
|
self.build_data_response(id, &buffer) // 构建 SSH_FXP_DATA
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题**:
|
||||||
|
1. ❌ 没有 maxpack 限制
|
||||||
|
2. ❌ 直接返回 client 请求的全部数据
|
||||||
|
3. ❌ Packet 大小超过 client maxpack
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 解决方案 ⭐⭐⭐⭐⭐
|
||||||
|
|
||||||
|
### 方案 1:修改 SftpHandler 结构(推荐)
|
||||||
|
|
||||||
|
**步骤 1**:添加 maxpack 字段到 SftpHandler
|
||||||
|
```rust
|
||||||
|
pub struct SftpHandler {
|
||||||
|
root_dir: PathBuf,
|
||||||
|
next_handle_id: u32,
|
||||||
|
handles: std::collections::HashMap<u32, SftpHandle>,
|
||||||
|
maxpacket: u32, // ⭐⭐⭐⭐⭐ Phase 4: 添加 maxpack 限制
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**步骤 2**:修改 SftpHandler::new() 方法
|
||||||
|
```rust
|
||||||
|
pub fn new(root_dir: PathBuf, maxpacket: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
root_dir,
|
||||||
|
next_handle_id: 0,
|
||||||
|
handles: std::collections::HashMap::new(),
|
||||||
|
maxpacket, // ⭐⭐⭐⭐⭐ Phase 4: 传入 maxpack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**步骤 3**:修改 handle_read() 方法
|
||||||
|
```rust
|
||||||
|
fn handle_read(&mut self, data: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
let length = cursor.read_u32::<BigEndian>()?;
|
||||||
|
|
||||||
|
// ⭐⭐⭐⭐⭐ Phase 4: 限制数据大小,不超过 maxpack - 1024
|
||||||
|
let max_read = self.maxpacket - 1024; // 1024 bytes header overhead
|
||||||
|
let actual_length = std::cmp::min(length, max_read);
|
||||||
|
|
||||||
|
let mut buffer = vec![0u8; actual_length as usize];
|
||||||
|
file.read(&mut buffer)
|
||||||
|
self.build_data_response(id, &buffer)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**步骤 4**:修改 channel.rs 中 SftpHandler 创建
|
||||||
|
```rust
|
||||||
|
// 从 Channel 中获取 remote_maxpacket
|
||||||
|
let maxpacket = channel.remote_maxpacket;
|
||||||
|
let sftp_handler = SftpHandler::new(root_dir, maxpacket);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 方案 2:修改 build_data_response(简化方案)
|
||||||
|
|
||||||
|
**步骤**:在 build_data_response 中检查 packet 大小
|
||||||
|
```rust
|
||||||
|
fn build_data_response(&self, id: u32, data: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
// ⭐⭐⭐⭐⭐ Phase 4: 检查 packet 大小
|
||||||
|
let max_data_size = 32000; // 约 32KB - header overhead
|
||||||
|
if data.len() > max_data_size {
|
||||||
|
warn!("Data size {} exceeds maxpack limit, truncating", data.len());
|
||||||
|
let truncated_data = &data[0..max_data_size];
|
||||||
|
// ... 构建 packet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 推荐方案:方案 1 ⭐⭐⭐⭐⭐
|
||||||
|
|
||||||
|
**理由**:
|
||||||
|
1. ✅ 符合 OpenSSH sftp-server.c 实现
|
||||||
|
2. ✅ 动态 maxpack(从 client 获取)
|
||||||
|
3. ✅ 灵活可配置
|
||||||
|
|
||||||
|
**预计工作量**:
|
||||||
|
- 修改文件:sftp_handler.rs, channel.rs
|
||||||
|
- 代码修改:约 50 行
|
||||||
|
- 测试验证:约 30 分钟
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 下一步行动 ⭐⭐⭐⭐⭐
|
||||||
|
|
||||||
|
**立即实施 Phase 4**:
|
||||||
|
1. Phase 4.1:添加 maxpack 字段到 SftpHandler(已完成:Channel 结构已存在)
|
||||||
|
2. Phase 4.2:修改 SftpHandler::new() 接受 maxpack 参数
|
||||||
|
3. Phase 4.3:修改 handle_read() 限制数据大小
|
||||||
|
4. Phase 4.4:修改 channel.rs 中 SftpHandler 创建
|
||||||
|
5. Phase 4.5:测试验证
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**最后更新**:2026-06-17 20:30
|
||||||
|
**问题严重性**:⭐⭐⭐⭐⭐ 极高
|
||||||
|
**下一步**:立即实施 Phase 4 修复
|
||||||
Reference in New Issue
Block a user