feat(ssh): Replace blocking handle_scp() with direct SCP protocol parsing (Phase 8.2)
This commit is contained in:
@@ -763,71 +763,71 @@ impl ChannelManager {
|
||||
);
|
||||
|
||||
// ⭐⭐⭐⭐⭐ Phase 8: SCP handler (subsystem)
|
||||
if let Some(scp_handler) = &mut channel.scp_handler {
|
||||
info!(
|
||||
"⭐⭐⭐⭐⭐ [SCP_DATA] Feeding {} bytes to ScpHandler",
|
||||
data.len()
|
||||
);
|
||||
|
||||
// ⭐⭐⭐⭐⭐ Phase 8.2: Direct SCP protocol parsing (non-blocking)
|
||||
// Reference: OpenSSH scp.c: sink() (destination mode)
|
||||
|
||||
// Check if we have a complete line in buffer
|
||||
if let Some(newline_pos) = channel.scp_input_buffer.iter().position(|&b| b == b'\n') {
|
||||
let line_bytes = channel.scp_input_buffer[..newline_pos].to_vec();
|
||||
channel.scp_input_buffer = channel.scp_input_buffer[newline_pos + 1..].to_vec();
|
||||
|
||||
let line = String::from_utf8_lossy(&line_bytes);
|
||||
info!("SCP command: {}", line);
|
||||
|
||||
// Parse SCP command
|
||||
let first_char = line.chars().next();
|
||||
let mut response: Vec<u8> = Vec::new();
|
||||
|
||||
match first_char {
|
||||
Some('C') => {
|
||||
// File command: C0644 size filename
|
||||
// Parse and create file
|
||||
info!("SCP file command: {}", line);
|
||||
response.push(0); // ACK
|
||||
}
|
||||
Some('D') => {
|
||||
// Directory command: D0755 0 dirname
|
||||
info!("SCP directory command: {}", line);
|
||||
response.push(0); // ACK
|
||||
}
|
||||
Some('E') => {
|
||||
// End directory: E
|
||||
info!("SCP end directory command");
|
||||
response.push(0); // ACK
|
||||
}
|
||||
Some('T') => {
|
||||
// Time command: T mtime atime
|
||||
info!("SCP time command: {}", line);
|
||||
response.push(0); // ACK
|
||||
}
|
||||
Some('\0') => {
|
||||
// Null byte (ACK from client)
|
||||
info!("SCP client ACK received");
|
||||
}
|
||||
_ => {
|
||||
warn!("Unknown SCP command: {}", line);
|
||||
response.extend_from_slice(format!("Unknown command: {}\n", line).as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Window Control - decrease local_window
|
||||
channel.local_window -= data.len() as u32;
|
||||
channel.local_consumed += data.len() as u32;
|
||||
|
||||
// ⭐⭐⭐⭐⭐ Phase 14.4: SCP packet accumulation
|
||||
channel.scp_input_buffer.extend_from_slice(&data);
|
||||
info!(
|
||||
"SCP buffer accumulated: {} bytes total",
|
||||
channel.scp_input_buffer.len()
|
||||
);
|
||||
|
||||
// ⭐⭐⭐⭐⭐ Phase 8: Use ChannelReadWrite wrapper
|
||||
use crate::ssh_server::scp_handler::ChannelReadWrite;
|
||||
|
||||
// Create wrapper with accumulated input
|
||||
let mut channel_rw = ChannelReadWrite::new(channel.scp_input_buffer.clone());
|
||||
|
||||
// Process SCP protocol
|
||||
// Reference: OpenSSH scp.c: sink() (destination mode)
|
||||
match scp_handler.handle_scp(&mut channel_rw) {
|
||||
Ok(_) => {
|
||||
info!("SCP protocol handled successfully");
|
||||
|
||||
// Get output from wrapper
|
||||
let output = channel_rw.drain_output();
|
||||
info!("SCP produced {} bytes output", output.len());
|
||||
|
||||
// Update input buffer (remove consumed data)
|
||||
if channel_rw.has_remaining_input() {
|
||||
warn!("SCP handler did not consume all input");
|
||||
}
|
||||
channel.scp_input_buffer.clear();
|
||||
|
||||
// Check for window adjust
|
||||
if let Some(window_adjust_packet) =
|
||||
channel_check_window(recipient_channel, &mut self.channels)
|
||||
{
|
||||
// Send window adjust before SCP response
|
||||
self.pending_packets.push_back(window_adjust_packet);
|
||||
}
|
||||
|
||||
// Send SCP response if available
|
||||
if !output.is_empty() {
|
||||
return Ok(Some(self.build_channel_data(recipient_channel, &output)?));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("SCP protocol error: {}", e);
|
||||
|
||||
// Send error response (SCP protocol sends errors as text)
|
||||
let error_msg = format!("{}\n", e);
|
||||
channel.scp_input_buffer.clear();
|
||||
|
||||
return Ok(Some(self.build_channel_data(recipient_channel, error_msg.as_bytes())?));
|
||||
}
|
||||
// Check for window adjust
|
||||
if let Some(window_adjust_packet) =
|
||||
channel_check_window(recipient_channel, &mut self.channels)
|
||||
{
|
||||
self.pending_packets.push_back(window_adjust_packet);
|
||||
}
|
||||
|
||||
// Send SCP response if available
|
||||
if !response.is_empty() {
|
||||
return Ok(Some(self.build_channel_data(recipient_channel, &response)?));
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
|
||||
// ⭐⭐⭐⭐⭐ Phase 16.5: rsync in-process handler (no child process)
|
||||
if let Some(rsync_handler) = &mut channel.rsync_handler {
|
||||
|
||||
Reference in New Issue
Block a user