Files
markbase/data/phase4_packet_size_issue_analysis.md
Warren 70353d2a55 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
2026-06-17 20:10:53 +08:00

4.2 KiB
Raw Blame History

Phase 4 问题分析SSH packet 大小超过 client maxpack 限制

问题严重性 极高


根本原因分析

问题定位

SSH_FXP_READ 请求

  • OpenSSH sftp client 默认请求读取长度:32768 bytes32KB
  • 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

问题

  1. 没有 maxpack 限制
  2. 直接返回 client 请求的全部数据
  3. 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

理由

  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 修复