frame.rs (12028B)
1 use crate::error::RadrootsSimplexSmpTransportError; 2 use alloc::vec::Vec; 3 use radroots_simplex_smp_proto::prelude::{ 4 RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, RadrootsSimplexSmpBrokerTransmission, 5 RadrootsSimplexSmpCommandTransmission, 6 }; 7 8 pub const RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE: usize = 16_384; 9 pub const RADROOTS_SIMPLEX_SMP_TRANSPORT_PAD_BYTE: u8 = b'#'; 10 const PADDED_PAYLOAD_PREFIX_LEN: usize = 2; 11 const MAX_TRANSPORT_PAYLOAD_LEN: usize = 12 RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE - PADDED_PAYLOAD_PREFIX_LEN; 13 14 #[derive(Debug, Clone, PartialEq, Eq)] 15 pub struct RadrootsSimplexSmpTransportBlock { 16 pub transmissions: Vec<Vec<u8>>, 17 } 18 19 impl RadrootsSimplexSmpTransportBlock { 20 pub fn new(transmissions: Vec<Vec<u8>>) -> Result<Self, RadrootsSimplexSmpTransportError> { 21 if transmissions.is_empty() { 22 return Err(RadrootsSimplexSmpTransportError::EmptyTransportBlock); 23 } 24 if transmissions.len() > u8::MAX as usize { 25 return Err(RadrootsSimplexSmpTransportError::TransmissionCountOverflow( 26 transmissions.len(), 27 )); 28 } 29 30 let mut payload_len = 1_usize; 31 for transmission in &transmissions { 32 if transmission.len() > u16::MAX as usize { 33 return Err(RadrootsSimplexSmpTransportError::TransmissionTooLarge( 34 transmission.len(), 35 )); 36 } 37 payload_len = payload_len.checked_add(2 + transmission.len()).ok_or( 38 RadrootsSimplexSmpTransportError::TransportPayloadTooLarge(usize::MAX), 39 )?; 40 } 41 if payload_len > MAX_TRANSPORT_PAYLOAD_LEN { 42 return Err(RadrootsSimplexSmpTransportError::TransportPayloadTooLarge( 43 payload_len, 44 )); 45 } 46 47 Ok(Self { transmissions }) 48 } 49 50 pub fn encode(&self) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 51 let payload = self.encode_payload()?; 52 encode_padded_bytes( 53 &payload, 54 RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE, 55 RADROOTS_SIMPLEX_SMP_TRANSPORT_PAD_BYTE, 56 ) 57 } 58 59 pub fn decode(bytes: &[u8]) -> Result<Self, RadrootsSimplexSmpTransportError> { 60 let payload = decode_padded_bytes( 61 bytes, 62 RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE, 63 RADROOTS_SIMPLEX_SMP_TRANSPORT_PAD_BYTE, 64 )?; 65 Self::from_payload(&payload) 66 } 67 68 pub fn encode_payload(&self) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 69 encode_transport_payload(&self.transmissions) 70 } 71 72 pub fn from_payload(payload: &[u8]) -> Result<Self, RadrootsSimplexSmpTransportError> { 73 let transmissions = decode_transport_payload(payload)?; 74 Self::new(transmissions) 75 } 76 77 pub fn from_command_transmissions( 78 transmissions: &[RadrootsSimplexSmpCommandTransmission], 79 transport_version: u16, 80 ) -> Result<Self, RadrootsSimplexSmpTransportError> { 81 let encoded = transmissions 82 .iter() 83 .map(|transmission| transmission.encode_for_version(transport_version)) 84 .collect::<Result<Vec<_>, _>>()?; 85 Self::new(encoded) 86 } 87 88 pub fn from_current_command_transmissions( 89 transmissions: &[RadrootsSimplexSmpCommandTransmission], 90 ) -> Result<Self, RadrootsSimplexSmpTransportError> { 91 Self::from_command_transmissions( 92 transmissions, 93 RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, 94 ) 95 } 96 97 pub fn decode_command_transmissions( 98 &self, 99 transport_version: u16, 100 ) -> Result<Vec<RadrootsSimplexSmpCommandTransmission>, RadrootsSimplexSmpTransportError> { 101 self.transmissions 102 .iter() 103 .map(|transmission| { 104 RadrootsSimplexSmpCommandTransmission::decode_for_version( 105 transport_version, 106 transmission, 107 ) 108 .map_err(Into::into) 109 }) 110 .collect() 111 } 112 113 pub fn from_broker_transmissions( 114 transmissions: &[RadrootsSimplexSmpBrokerTransmission], 115 transport_version: u16, 116 ) -> Result<Self, RadrootsSimplexSmpTransportError> { 117 let encoded = transmissions 118 .iter() 119 .map(|transmission| transmission.encode_for_version(transport_version)) 120 .collect::<Result<Vec<_>, _>>()?; 121 Self::new(encoded) 122 } 123 124 pub fn decode_broker_transmissions( 125 &self, 126 transport_version: u16, 127 ) -> Result<Vec<RadrootsSimplexSmpBrokerTransmission>, RadrootsSimplexSmpTransportError> { 128 self.transmissions 129 .iter() 130 .map(|transmission| { 131 RadrootsSimplexSmpBrokerTransmission::decode_for_version( 132 transport_version, 133 transmission, 134 ) 135 .map_err(Into::into) 136 }) 137 .collect() 138 } 139 } 140 141 pub fn encode_padded_bytes( 142 payload: &[u8], 143 padded_len: usize, 144 pad_byte: u8, 145 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 146 let max_payload_len = padded_len.checked_sub(PADDED_PAYLOAD_PREFIX_LEN).ok_or( 147 RadrootsSimplexSmpTransportError::TransportPayloadTooLarge(payload.len()), 148 )?; 149 if payload.len() > max_payload_len || payload.len() > u16::MAX as usize { 150 return Err(RadrootsSimplexSmpTransportError::TransportPayloadTooLarge( 151 payload.len(), 152 )); 153 } 154 155 let mut buffer = Vec::with_capacity(padded_len); 156 buffer.extend_from_slice(&(payload.len() as u16).to_be_bytes()); 157 buffer.extend_from_slice(payload); 158 buffer.resize(padded_len, pad_byte); 159 Ok(buffer) 160 } 161 162 pub fn decode_padded_bytes( 163 bytes: &[u8], 164 padded_len: usize, 165 pad_byte: u8, 166 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 167 if bytes.len() != padded_len { 168 return Err(RadrootsSimplexSmpTransportError::InvalidPaddedBlockLength { 169 expected: padded_len, 170 actual: bytes.len(), 171 }); 172 } 173 let Some(length_bytes) = bytes.get(..2) else { 174 return Err(RadrootsSimplexSmpTransportError::InvalidPaddedBlockLength { 175 expected: padded_len, 176 actual: bytes.len(), 177 }); 178 }; 179 let payload_len = u16::from_be_bytes([length_bytes[0], length_bytes[1]]) as usize; 180 let max_payload_len = padded_len - PADDED_PAYLOAD_PREFIX_LEN; 181 if payload_len > max_payload_len { 182 return Err(RadrootsSimplexSmpTransportError::TransportPayloadTooLarge( 183 payload_len, 184 )); 185 } 186 let end = PADDED_PAYLOAD_PREFIX_LEN + payload_len; 187 for (offset, byte) in bytes[end..].iter().enumerate() { 188 if *byte != pad_byte { 189 return Err(RadrootsSimplexSmpTransportError::InvalidPadding { 190 index: end + offset, 191 value: *byte, 192 }); 193 } 194 } 195 Ok(bytes[PADDED_PAYLOAD_PREFIX_LEN..end].to_vec()) 196 } 197 198 fn encode_transport_payload( 199 transmissions: &[Vec<u8>], 200 ) -> Result<Vec<u8>, RadrootsSimplexSmpTransportError> { 201 if transmissions.is_empty() { 202 return Err(RadrootsSimplexSmpTransportError::EmptyTransportBlock); 203 } 204 if transmissions.len() > u8::MAX as usize { 205 return Err(RadrootsSimplexSmpTransportError::TransmissionCountOverflow( 206 transmissions.len(), 207 )); 208 } 209 210 let mut buffer = Vec::new(); 211 buffer.push(transmissions.len() as u8); 212 for transmission in transmissions { 213 if transmission.len() > u16::MAX as usize { 214 return Err(RadrootsSimplexSmpTransportError::TransmissionTooLarge( 215 transmission.len(), 216 )); 217 } 218 buffer.extend_from_slice(&(transmission.len() as u16).to_be_bytes()); 219 buffer.extend_from_slice(transmission); 220 } 221 if buffer.len() > MAX_TRANSPORT_PAYLOAD_LEN { 222 return Err(RadrootsSimplexSmpTransportError::TransportPayloadTooLarge( 223 buffer.len(), 224 )); 225 } 226 Ok(buffer) 227 } 228 229 fn decode_transport_payload( 230 payload: &[u8], 231 ) -> Result<Vec<Vec<u8>>, RadrootsSimplexSmpTransportError> { 232 let Some((&declared_count, mut remainder)) = payload.split_first() else { 233 return Err(RadrootsSimplexSmpTransportError::EmptyTransportBlock); 234 }; 235 if declared_count == 0 { 236 return Err(RadrootsSimplexSmpTransportError::EmptyTransportBlock); 237 } 238 239 let mut transmissions = Vec::with_capacity(declared_count as usize); 240 for _ in 0..declared_count { 241 let Some(length_bytes) = remainder.get(..2) else { 242 return Err( 243 radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpProtoError::UnexpectedEof 244 .into(), 245 ); 246 }; 247 let transmission_len = u16::from_be_bytes([length_bytes[0], length_bytes[1]]) as usize; 248 let Some(transmission) = remainder.get(2..2 + transmission_len) else { 249 return Err( 250 radroots_simplex_smp_proto::prelude::RadrootsSimplexSmpProtoError::UnexpectedEof 251 .into(), 252 ); 253 }; 254 transmissions.push(transmission.to_vec()); 255 remainder = &remainder[2 + transmission_len..]; 256 } 257 258 if !remainder.is_empty() { 259 return Err(RadrootsSimplexSmpTransportError::TrailingTransportBytes( 260 remainder.len(), 261 )); 262 } 263 if transmissions.len() != declared_count as usize { 264 return Err( 265 RadrootsSimplexSmpTransportError::UnexpectedTransmissionCount { 266 declared: declared_count, 267 actual: transmissions.len(), 268 }, 269 ); 270 } 271 Ok(transmissions) 272 } 273 274 #[cfg(test)] 275 mod tests { 276 use super::*; 277 use radroots_simplex_smp_proto::prelude::{ 278 RadrootsSimplexSmpCommand, RadrootsSimplexSmpCommandTransmission, 279 RadrootsSimplexSmpCorrelationId, 280 }; 281 282 #[test] 283 fn roundtrips_command_transmissions_through_transport_block() { 284 let transmissions = vec![ 285 RadrootsSimplexSmpCommandTransmission { 286 authorization: b"sig-a".to_vec(), 287 correlation_id: Some(RadrootsSimplexSmpCorrelationId::new([7_u8; 24])), 288 entity_id: b"queue-a".to_vec(), 289 command: RadrootsSimplexSmpCommand::Ping, 290 }, 291 RadrootsSimplexSmpCommandTransmission { 292 authorization: b"sig-b".to_vec(), 293 correlation_id: Some(RadrootsSimplexSmpCorrelationId::new([9_u8; 24])), 294 entity_id: b"queue-b".to_vec(), 295 command: RadrootsSimplexSmpCommand::Get, 296 }, 297 ]; 298 299 let block = 300 RadrootsSimplexSmpTransportBlock::from_current_command_transmissions(&transmissions) 301 .unwrap(); 302 let encoded = block.encode().unwrap(); 303 assert_eq!(encoded.len(), RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE); 304 305 let decoded = RadrootsSimplexSmpTransportBlock::decode(&encoded).unwrap(); 306 let roundtrip = decoded 307 .decode_command_transmissions(RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION) 308 .unwrap(); 309 assert_eq!(roundtrip, transmissions); 310 } 311 312 #[test] 313 fn rejects_invalid_padding() { 314 let block = RadrootsSimplexSmpTransportBlock::new(vec![b"PING".to_vec()]).unwrap(); 315 let mut encoded = block.encode().unwrap(); 316 encoded[RADROOTS_SIMPLEX_SMP_TRANSPORT_BLOCK_SIZE - 1] = b'!'; 317 318 let error = RadrootsSimplexSmpTransportBlock::decode(&encoded).unwrap_err(); 319 assert!(matches!( 320 error, 321 RadrootsSimplexSmpTransportError::InvalidPadding { .. } 322 )); 323 } 324 325 #[test] 326 fn roundtrips_transport_payload_without_padding() { 327 let block = 328 RadrootsSimplexSmpTransportBlock::new(vec![b"one".to_vec(), b"two".to_vec()]).unwrap(); 329 let payload = block.encode_payload().unwrap(); 330 let decoded = RadrootsSimplexSmpTransportBlock::from_payload(&payload).unwrap(); 331 assert_eq!(decoded, block); 332 } 333 }