- 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
4.2 KiB
4.2 KiB
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):
/* 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):
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
问题:
- ❌ 没有 maxpack 限制
- ❌ 直接返回 client 请求的全部数据
- ❌ Packet 大小超过 client maxpack
解决方案 ⭐⭐⭐⭐⭐
方案 1:修改 SftpHandler 结构(推荐)
步骤 1:添加 maxpack 字段到 SftpHandler
pub struct SftpHandler {
root_dir: PathBuf,
next_handle_id: u32,
handles: std::collections::HashMap<u32, SftpHandle>,
maxpacket: u32, // ⭐⭐⭐⭐⭐ Phase 4: 添加 maxpack 限制
}
步骤 2:修改 SftpHandler::new() 方法
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() 方法
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 创建
// 从 Channel 中获取 remote_maxpacket
let maxpacket = channel.remote_maxpacket;
let sftp_handler = SftpHandler::new(root_dir, maxpacket);
方案 2:修改 build_data_response(简化方案)
步骤:在 build_data_response 中检查 packet 大小
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 ⭐⭐⭐⭐⭐
理由:
- ✅ 符合 OpenSSH sftp-server.c 实现
- ✅ 动态 maxpack(从 client 获取)
- ✅ 灵活可配置
预计工作量:
- 修改文件:sftp_handler.rs, channel.rs
- 代码修改:约 50 行
- 测试验证:约 30 分钟
下一步行动 ⭐⭐⭐⭐⭐
立即实施 Phase 4:
- Phase 4.1:添加 maxpack 字段到 SftpHandler(已完成:Channel 结构已存在)
- Phase 4.2:修改 SftpHandler::new() 接受 maxpack 参数
- Phase 4.3:修改 handle_read() 限制数据大小
- Phase 4.4:修改 channel.rs 中 SftpHandler 创建
- Phase 4.5:测试验证
最后更新:2026-06-17 20:30
问题严重性:⭐⭐⭐⭐⭐ 极高
下一步:立即实施 Phase 4 修复