/ firmware / src / services / ssh / channel.rs
channel.rs
  1  use super::transport::Transport;
  2  use super::types::{Behavior, Request, TransportError};
  3  use super::wire::into_u32;
  4  
  5  /// Destination of data written to a channel.
  6  #[derive(Debug, Clone, Copy)]
  7  pub enum Pipe {
  8      /// Standard output.
  9      Stdout,
 10      /// Standard error.
 11      Stderr,
 12  }
 13  
 14  /// Channel associated with an SSH transport.
 15  pub struct Channel<'a, 'b, T: Behavior> {
 16      transport: &'b mut Transport<'a, T>,
 17  }
 18  
 19  impl<'a, 'b, T: Behavior> Channel<'a, 'b, T> {
 20      pub(crate) fn new(transport: &'b mut Transport<'a, T>) -> Self {
 21          Self { transport }
 22      }
 23  
 24      /// Returns the request associated with this channel.
 25      pub fn request(&self) -> Request<T::Command> {
 26          self.transport.channel_request()
 27      }
 28  
 29      /// Returns the user associated with this channel.
 30      pub fn user(&self) -> T::User {
 31          self.transport.channel_user()
 32      }
 33  
 34      /// Returns the identification string of the client.
 35      pub fn client_ssh_id_string(&self) -> &str {
 36          self.transport.client_ssh_id_string()
 37      }
 38  
 39      /// Closes the channel with a given exit status.
 40      pub async fn exit(self, exit_status: u32) -> Result<(), TransportError<T>> {
 41          self.transport.channel_exit(exit_status).await
 42      }
 43  
 44      /// Obtains a reader over this channel.
 45      pub async fn reader(
 46          &mut self,
 47          len: Option<usize>,
 48      ) -> Result<Reader<'a, '_, T>, TransportError<T>> {
 49          self.transport.channel_adjust(len.map(into_u32)).await?;
 50          Ok(Reader::new(self.transport)) // must read until done
 51      }
 52  
 53      /// Convenience method for exact-or-until-EOF reads.
 54      ///
 55      /// This method reads up to an exact number of bytes from the channel, stopping early
 56      /// only in the case of EOF, and returns the number of bytes that were actually read.
 57      pub async fn read_exact_stdin(
 58          &mut self,
 59          mut bytes: &mut [u8],
 60      ) -> Result<usize, TransportError<T>> {
 61          let read_len = bytes.len();
 62  
 63          let mut reader = self.reader(Some(read_len)).await?;
 64  
 65          while let Some(read) = reader.read().await? {
 66              let (dest, remaining) = bytes.split_at_mut(read.len());
 67              dest.copy_from_slice(read);
 68              bytes = remaining;
 69          }
 70  
 71          Ok(read_len - bytes.len())
 72      }
 73  
 74      /// Obtains a writer over this channel for the specified pipe.
 75      pub fn writer(&mut self, pipe: Pipe) -> Writer<'a, '_, T> {
 76          Writer::new(self.transport, pipe)
 77      }
 78  
 79      /// Obtains a writer over this channel for standard output.
 80      pub fn stdout(&mut self) -> Writer<'a, '_, T> {
 81          self.writer(Pipe::Stdout)
 82      }
 83  
 84      /// Obtains a writer over this channel for standard error.
 85      pub fn stderr(&mut self) -> Writer<'a, '_, T> {
 86          self.writer(Pipe::Stderr)
 87      }
 88  
 89      /// Convenience method that writes all bytes into standard output.
 90      pub async fn write_all_stdout(&mut self, bytes: &[u8]) -> Result<(), TransportError<T>> {
 91          self.stdout().write_all_internal(bytes).await
 92      }
 93  
 94      /// Convenience method that writes all bytes into standard error.
 95      pub async fn write_all_stderr(&mut self, bytes: &[u8]) -> Result<(), TransportError<T>> {
 96          self.stderr().write_all_internal(bytes).await
 97      }
 98  }
 99  
100  /// Reader associated with an SSH channel.
101  pub struct Reader<'a, 'b, T: Behavior> {
102      transport: &'b mut Transport<'a, T>,
103  }
104  
105  impl<'a, 'b, T: Behavior> Reader<'a, 'b, T> {
106      fn new(transport: &'b mut Transport<'a, T>) -> Self {
107          Self { transport }
108      }
109  
110      /// Reads data from the underlying channel.
111      ///
112      /// This method will never read more data than was originally requested upon
113      /// construction of the reader object, or `Ok(None)` if no more data or EOF.
114      pub async fn read(&mut self) -> Result<Option<&[u8]>, TransportError<T>> {
115          self.transport.channel_read().await
116      }
117  
118      /// Returns whether the channel's receiving half has reached EOF.
119      pub fn is_eof(&mut self) -> bool {
120          self.transport.channel_is_eof()
121      }
122  }
123  
124  /// Writer associated with an SSH channel.
125  pub struct Writer<'a, 'b, T: Behavior> {
126      transport: &'b mut Transport<'a, T>,
127      pipe: Pipe,
128  }
129  
130  impl<'a, 'b, T: Behavior> Writer<'a, 'b, T> {
131      fn new(transport: &'b mut Transport<'a, T>, pipe: Pipe) -> Self {
132          Self { transport, pipe }
133      }
134  
135      /// Returns a byte slice into the packet buffer.
136      pub fn buffer(&mut self) -> &mut [u8] {
137          self.transport.channel_data_payload_buffer(self.pipe)
138      }
139  
140      /// Writes all requested bytes already present in the packet buffer.
141      ///
142      /// This will take the first `len` bytes in the byte slice returned
143      /// by the `buffer()` method and send them as a single SSH message.
144      pub async fn write_all(self, len: usize) -> Result<(), TransportError<T>> {
145          self.transport.channel_write_all(len, self.pipe).await?;
146  
147          // TODO: report to the caller whether we wrote all bytes (the only
148          // case where we don't is if the client closed the channel on us).
149  
150          Ok(())
151      }
152  
153      async fn write_all_internal(mut self, bytes: &[u8]) -> Result<(), TransportError<T>> {
154          for chunk in bytes.chunks(self.buffer().len()) {
155              self.buffer()[..chunk.len()].copy_from_slice(chunk);
156              self.transport
157                  .channel_write_all(chunk.len(), self.pipe)
158                  .await?;
159          }
160  
161          // TODO: report to the caller whether we wrote all bytes (the only
162          // case where we don't is if the client closed the channel on us).
163  
164          Ok(())
165      }
166  }