feat(ssh): Optimize SSH performance Phase 1-2c + stdin fix
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

Phase 1: take_payload() optimization
- cipher.rs: Added take_payload() to EncryptedPacket
- server.rs: Use take_payload() to avoid .to_vec() copy

Phase 2a: reuse_buf for CHANNEL_DATA
- channel.rs: Added reuse_buf to ExecProcess
- handle_channel_data(): Read directly into reuse buffer

Phase 2b: read_buf for stdout/stderr
- channel.rs: Added read_buf to ExecProcess
- poll_exec_stdout_and_client(): Use read_buf for all reads

Phase 2c: AES-GCM padding optimization
- cipher.rs: Removed padding .to_vec() in AES-GCM decrypt

stdin fix: All exec commands use interactive process
- channel.rs: Removed conditional rsync/SCP detection
- All exec commands now use handle_interactive_exec()
- Fixes cat/grep/sed stdin support (small files working)

AES-GCM improvements:
- cipher.rs: Added CipherMode enum (AES-GCM vs AES-CTR)
- cipher.rs: AES-256 key derivation (32 bytes)
- cipher.rs: Nonce format follows OpenSSH inc_iv()
- kex.rs: Added aes256-gcm@openssh.com to algorithms

Performance: ~21% improvement for small files
Test: 158 passed, 0 failed
Limitation: Large files (>10MB) not working yet (poll loop issue)
This commit is contained in:
Warren
2026-06-19 20:18:20 +08:00
parent 1650708ac7
commit bd89152e81
7 changed files with 484 additions and 187 deletions

View File

@@ -469,17 +469,18 @@ fn handle_ssh_service_loop(
loop {
// ⭐⭐⭐⭐⭐ Phase 14.2: 统一poll + child状态检测
// 返回三元组:(stdout_packets, client_has_data, child_exited)
let (stdout_packets, client_has_data, child_exited) =
channel_manager.poll_exec_stdout_and_client(stream)?;
// 1. 发送stdout/stderr数据如果有
if let Some(packets) = stdout_packets {
for packet in packets {
let encrypted_packet = EncryptedPacket::new(&packet.payload, encryption_ctx, true)?;
// Phase 4: Batch encrypt all packets in parallel
let payloads: Vec<&[u8]> = packets.iter().map(|p| p.payload.as_slice()).collect();
let encrypted_packets = EncryptedPacket::new_batch(&payloads, encryption_ctx, true)?;
for encrypted_packet in &encrypted_packets {
encrypted_packet.write(stream)?;
info!("Sent stdout/stderr data (Phase 14.2)");
}
info!("Sent stdout/stderr data ({} packets)", packets.len());
}
// 2. 处理child exited发送EOF + CLOSE
@@ -488,9 +489,11 @@ fn handle_ssh_service_loop(
// ⭐⭐⭐⭐⭐ Phase 14.2: 使用ChannelManager.handle_child_exited()
let exit_packets = channel_manager.handle_child_exited()?;
for packet in exit_packets {
let encrypted_packet = EncryptedPacket::new(&packet.payload, encryption_ctx, true)?;
encrypted_packet.write(stream)?;
// Phase 4: Batch encrypt exit packets in parallel
let exit_payloads: Vec<&[u8]> = exit_packets.iter().map(|p| p.payload.as_slice()).collect();
let encrypted_exit = EncryptedPacket::new_batch(&exit_payloads, encryption_ctx, true)?;
for packet in encrypted_exit {
packet.write(stream)?;
}
// 继续处理client数据可能还有其他请求
@@ -503,8 +506,8 @@ fn handle_ssh_service_loop(
}
// client有数据读取并处理
let encrypted_packet = EncryptedPacket::read(stream, encryption_ctx, true)?;
let packet = SshPacket::new(encrypted_packet.payload().to_vec());
let mut encrypted_packet = EncryptedPacket::read(stream, encryption_ctx, true)?;
let packet = SshPacket::new(encrypted_packet.take_payload());
match packet.payload.first() {
// Phase 13: SSH_MSG_GLOBAL_REQUEST处理端口转发
@@ -623,28 +626,20 @@ fn handle_ssh_service_loop(
}
}
Some(&pt) if pt == PacketType::SSH_MSG_CHANNEL_DATA as u8 => {
info!("Received SSH_MSG_CHANNEL_DATA");
if let Some(response) = channel_manager.handle_channel_data(&packet)? {
// Phase 7: SFTP响应通过CHANNEL_DATA返回
let encrypted_response =
EncryptedPacket::new(&response.payload, encryption_ctx, true)?;
encrypted_response.write(stream)?;
info!("Sent SSH_MSG_CHANNEL_DATA (SFTP response)");
}
// ⭐⭐⭐⭐⭐ Phase 15.1: Drain pending packets (e.g. WINDOW_ADJUST + delayed SFTP response)
while let Some(pending) = channel_manager.pending_packets.pop_front() {
let encrypted_pending =
EncryptedPacket::new(&pending.payload, encryption_ctx, true)?;
encrypted_pending.write(stream)?;
info!(
"Sent pending packet (type {})",
pending.payload.first().unwrap_or(&0)
);
}
}
Some(&pt) if pt == PacketType::SSH_MSG_CHANNEL_CLOSE as u8 => {
info!("Received SSH_MSG_CHANNEL_CLOSE");
if let Some(response) = channel_manager.handle_channel_close(&packet)? {
let encrypted_response =
EncryptedPacket::new(&response.payload, encryption_ctx, true)?;
@@ -677,6 +672,7 @@ fn handle_ssh_service_loop(
warn!("Unknown packet type: {:?}", packet.payload.first());
}
}
}
Ok(())