1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
use std::collections::VecDeque;
use std::io;
use puffin::codec;
use puffin::codec::Codec;
use puffin::protocol::ProtocolMessageDeframer;
use crate::protocol::SshProtocolTypes;
use crate::ssh::message::{OnWireData, RawSshMessage};
const MAX_WIRE_SIZE: usize = 35000;
/// This deframer works to reconstruct SSH messages
/// from arbitrary-sized reads, buffering as necessary.
/// The input is `read()`, the output is the `frames` deque.
pub struct SshMessageDeframer {
/// Completed frames for output.
pub frames: VecDeque<RawSshMessage>,
/// Set to true if the peer is not talking SSH, but some other
/// protocol. The caller should abort the connection, because
/// the deframer cannot recover.
pub desynced: bool,
/// A fixed-size buffer containing the currently-accumulating
/// TLS message.
buf: Box<[u8; MAX_WIRE_SIZE]>,
/// What size prefix of `buf` is used.
used: usize,
}
enum BufferContents {
/// Contains an invalid message as a header.
Invalid,
/// Might contain a valid message if we receive more.
/// Perhaps totally empty!
Partial,
/// Contains a valid frame as a prefix.
Valid,
}
impl Default for SshMessageDeframer {
fn default() -> Self {
Self::new()
}
}
impl SshMessageDeframer {
pub fn new() -> Self {
Self {
frames: VecDeque::new(),
desynced: false,
buf: Box::new([0u8; MAX_WIRE_SIZE]),
used: 0,
}
}
/// Read some bytes from `rd`, and add them to our internal
/// buffer. If this means our internal buffer contains
/// full messages, decode them all.
pub fn read(&mut self, rd: &mut dyn io::Read) -> io::Result<usize> {
// Try to do the largest reads possible. Note that if
// we get a message with a length field out of range here,
// we do a zero length read. That looks like an EOF to
// the next layer up, which is fine.
debug_assert!(self.used <= MAX_WIRE_SIZE);
let new_bytes = rd.read(&mut self.buf[self.used..])?;
self.used += new_bytes;
loop {
if self.used == 0 {
break;
}
match self.try_deframe_one() {
BufferContents::Invalid => {
println!("ufferContents::Invalid");
self.desynced = true;
break;
}
BufferContents::Valid => continue,
BufferContents::Partial => break,
}
}
Ok(new_bytes)
}
/// Returns true if we have messages for the caller
/// to process, either whole messages in our output
/// queue or partial messages in our buffer.
pub fn has_pending(&self) -> bool {
!self.frames.is_empty() || self.used > 0
}
/// Does our `buf` contain a full message? It does if it is big enough to
/// contain a header, and that header has a length which falls within `buf`.
/// If so, deframe it and place the message onto the frames output queue.
fn try_deframe_one(&mut self) -> BufferContents {
// Try to decode a message off the front of buf.
let mut rd = codec::Reader::init(&self.buf[..self.used]);
match RawSshMessage::read(&mut rd) {
Some(m) => {
let used = rd.used();
self.frames.push_back(m);
self.buf_consume(used);
BufferContents::Valid
}
None => {
self.frames
.push_back(RawSshMessage::OnWire(OnWireData(Vec::from(
&self.buf[..self.used],
))));
self.buf_consume(self.used);
BufferContents::Valid
//BufferContents::Invalid
}
}
}
#[allow(clippy::comparison_chain)]
fn buf_consume(&mut self, taken: usize) {
if taken < self.used {
/* Before:
* +----------+----------+----------+
* | taken | pending |xxxxxxxxxx|
* +----------+----------+----------+
* 0 ^ taken ^ self.used
*
* After:
* +----------+----------+----------+
* | pending |xxxxxxxxxxxxxxxxxxxxx|
* +----------+----------+----------+
* 0 ^ self.used
*/
self.buf.copy_within(taken..self.used, 0);
self.used -= taken;
} else if taken == self.used {
self.used = 0;
}
}
}
impl ProtocolMessageDeframer<SshProtocolTypes> for SshMessageDeframer {
type OpaqueProtocolMessage = RawSshMessage;
fn pop_frame(&mut self) -> Option<RawSshMessage> {
self.frames.pop_front()
}
fn read(&mut self, rd: &mut dyn std::io::Read) -> std::io::Result<usize> {
self.read(rd)
}
}