use std::collections::VecDeque; use tracing::{debug, warn}; use crate::client_error; use crate::packet::Packet; use crate::parse::{Parser, Writer}; use crate::Result; pub(crate) struct ServerChannelsState { packets_to_send: VecDeque, channels: Vec, } struct SessionChannel { peer_channel: u32, has_pty: bool, has_shell: bool, sent_bytes: Vec, } impl ServerChannelsState { pub(crate) fn new() -> Self { ServerChannelsState { packets_to_send: VecDeque::new(), channels: Vec::new(), } } pub(crate) fn on_packet(&mut self, packet_type: u8, mut payload: Parser<'_>) -> Result<()> { match packet_type { Packet::SSH_MSG_CHANNEL_OPEN => { // let channel_type = payload.utf8_string()?; let sender_channel = payload.u32()?; let initial_window_size = payload.u32()?; let max_packet_size = payload.u32()?; debug!(?channel_type, ?sender_channel, "Opening channel"); match channel_type { "session" => { let our_number = self.channels.len() as u32; let mut confirm = Writer::new(); confirm.u8(Packet::SSH_MSG_CHANNEL_OPEN_CONFIRMATION); confirm.u32(our_number); confirm.u32(sender_channel); confirm.u32(initial_window_size); confirm.u32(max_packet_size); self.channels.push(SessionChannel { peer_channel: sender_channel, has_pty: false, has_shell: false, sent_bytes: Vec::new(), }); self.packets_to_send.push_back(Packet { payload: confirm.finish(), }); debug!(?channel_type, ?our_number, "Successfully opened channel"); } _ => { let mut failure = Writer::new(); failure.u8(Packet::SSH_MSG_CHANNEL_OPEN_FAILURE); failure.u32(sender_channel); failure.u32(3); // SSH_OPEN_UNKNOWN_CHANNEL_TYPE failure.string(b"unknown channel type"); failure.string(b"en_US"); self.packets_to_send.push_back(Packet { payload: failure.finish(), }); } } } Packet::SSH_MSG_CHANNEL_DATA => { let our_channel = payload.u32()?; let data = payload.string()?; let channel = self.channel(our_channel)?; channel.recv_bytes(data); let mut reply = Writer::new(); reply.u8(Packet::SSH_MSG_CHANNEL_DATA); reply.u32(channel.peer_channel); reply.string(data); self.packets_to_send.push_back(Packet { payload: reply.finish(), }); } Packet::SSH_MSG_CHANNEL_REQUEST => { let our_channel = payload.u32()?; let request_type = payload.utf8_string()?; let want_reply = payload.bool()?; debug!(?our_channel, ?request_type, "Got channel request"); let channel = self.channel(our_channel)?; let peer_channel = channel.peer_channel; match request_type { "pty-req" => { let term = payload.utf8_string()?; let width_chars = payload.u32()?; let height_rows = payload.u32()?; let _width_px = payload.u32()?; let _height_px = payload.u32()?; let _term_modes = payload.string()?; debug!( ?our_channel, ?term, ?width_chars, ?height_rows, "Trying to open a terminal" ); // Faithfully allocate the PTY. channel.has_pty = true; if want_reply { self.send_channel_success(peer_channel); } } "shell" => { if !channel.has_pty { self.send_channel_failure(peer_channel); } // Sure! (reborrow) let channel = self.channel(our_channel)?; channel.has_shell = true; debug!(?our_channel, "Opening shell"); if want_reply { self.send_channel_success(peer_channel); } } "signal" => { debug!(?our_channel, "Received signal"); // Ignore signals, something we can do. } _ => { warn!(?request_type, ?our_channel, "Unknown channel request"); self.send_channel_failure(peer_channel); } } } _ => { todo!("{packet_type}"); } } Ok(()) } pub(crate) fn packets_to_send(&mut self) -> impl Iterator + '_ { self.packets_to_send.drain(..) } fn send_channel_success(&mut self, recipient_channel: u32) { let mut failure = Writer::new(); failure.u8(Packet::SSH_MSG_CHANNEL_SUCCESS); failure.u32(recipient_channel); self.packets_to_send.push_back(Packet { payload: failure.finish(), }); } fn send_channel_failure(&mut self, recipient_channel: u32) { let mut failure = Writer::new(); failure.u8(Packet::SSH_MSG_CHANNEL_FAILURE); failure.u32(recipient_channel); self.packets_to_send.push_back(Packet { payload: failure.finish(), }); } fn channel(&mut self, number: u32) -> Result<&mut SessionChannel> { self.channels .get_mut(number as usize) .ok_or_else(|| client_error!("unknown channel: {number}")) } } impl SessionChannel { fn recv_bytes(&mut self, bytes: &[u8]) { self.sent_bytes.extend_from_slice(bytes); } }