1*4185b066SAndroid Build Coastguard Worker // Copyright 2024 Google LLC 2*4185b066SAndroid Build Coastguard Worker // 3*4185b066SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); 4*4185b066SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License. 5*4185b066SAndroid Build Coastguard Worker // You may obtain a copy of the License at 6*4185b066SAndroid Build Coastguard Worker // 7*4185b066SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0 8*4185b066SAndroid Build Coastguard Worker // 9*4185b066SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software 10*4185b066SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, 11*4185b066SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*4185b066SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and 13*4185b066SAndroid Build Coastguard Worker // limitations under the License. 14*4185b066SAndroid Build Coastguard Worker 15*4185b066SAndroid Build Coastguard Worker //! Helpers for message fragmentation and reassembly. 16*4185b066SAndroid Build Coastguard Worker 17*4185b066SAndroid Build Coastguard Worker use alloc::borrow::Cow; 18*4185b066SAndroid Build Coastguard Worker use alloc::vec::Vec; 19*4185b066SAndroid Build Coastguard Worker 20*4185b066SAndroid Build Coastguard Worker /// Prefix byte indicating more data is to come. 21*4185b066SAndroid Build Coastguard Worker const PREFIX_MORE_TO_COME: u8 = 0xcc; // 'ccontinues' 22*4185b066SAndroid Build Coastguard Worker /// Prefix byte indicating that this is the final fragment. 23*4185b066SAndroid Build Coastguard Worker const PREFIX_FINAL_FRAGMENT: u8 = 0xdd; // 'ddone' 24*4185b066SAndroid Build Coastguard Worker 25*4185b066SAndroid Build Coastguard Worker /// Empty placeholder message indicating more data is due. 26*4185b066SAndroid Build Coastguard Worker pub const PLACEHOLDER_MORE_TO_COME: &[u8] = &[PREFIX_MORE_TO_COME]; 27*4185b066SAndroid Build Coastguard Worker 28*4185b066SAndroid Build Coastguard Worker /// Helper to emit a single message in fragments. 29*4185b066SAndroid Build Coastguard Worker pub struct Fragmenter<'a> { 30*4185b066SAndroid Build Coastguard Worker data: &'a [u8], 31*4185b066SAndroid Build Coastguard Worker max_size: usize, 32*4185b066SAndroid Build Coastguard Worker } 33*4185b066SAndroid Build Coastguard Worker 34*4185b066SAndroid Build Coastguard Worker impl<'a> Fragmenter<'a> { 35*4185b066SAndroid Build Coastguard Worker /// Create a fragmentation iterator for the given data. new(data: &'a [u8], max_size: usize) -> Fragmenter<'a>36*4185b066SAndroid Build Coastguard Worker pub fn new(data: &'a [u8], max_size: usize) -> Fragmenter<'a> { 37*4185b066SAndroid Build Coastguard Worker assert!(max_size > 1); 38*4185b066SAndroid Build Coastguard Worker Self { data, max_size } 39*4185b066SAndroid Build Coastguard Worker } 40*4185b066SAndroid Build Coastguard Worker } 41*4185b066SAndroid Build Coastguard Worker 42*4185b066SAndroid Build Coastguard Worker impl<'a> Iterator for Fragmenter<'a> { 43*4185b066SAndroid Build Coastguard Worker type Item = Vec<u8>; next(&mut self) -> Option<Self::Item>44*4185b066SAndroid Build Coastguard Worker fn next(&mut self) -> Option<Self::Item> { 45*4185b066SAndroid Build Coastguard Worker if self.data.is_empty() { 46*4185b066SAndroid Build Coastguard Worker None 47*4185b066SAndroid Build Coastguard Worker } else { 48*4185b066SAndroid Build Coastguard Worker let consume = core::cmp::min(self.max_size - 1, self.data.len()); 49*4185b066SAndroid Build Coastguard Worker let marker = 50*4185b066SAndroid Build Coastguard Worker if consume < self.data.len() { PREFIX_MORE_TO_COME } else { PREFIX_FINAL_FRAGMENT }; 51*4185b066SAndroid Build Coastguard Worker let mut result = Vec::with_capacity(consume + 1); 52*4185b066SAndroid Build Coastguard Worker result.push(marker); 53*4185b066SAndroid Build Coastguard Worker result.extend_from_slice(&self.data[..consume]); 54*4185b066SAndroid Build Coastguard Worker self.data = &self.data[consume..]; 55*4185b066SAndroid Build Coastguard Worker Some(result) 56*4185b066SAndroid Build Coastguard Worker } 57*4185b066SAndroid Build Coastguard Worker } 58*4185b066SAndroid Build Coastguard Worker } 59*4185b066SAndroid Build Coastguard Worker 60*4185b066SAndroid Build Coastguard Worker /// Buffer to accumulate fragmented messages. 61*4185b066SAndroid Build Coastguard Worker #[derive(Default)] 62*4185b066SAndroid Build Coastguard Worker pub struct Reassembler(Vec<u8>); 63*4185b066SAndroid Build Coastguard Worker 64*4185b066SAndroid Build Coastguard Worker impl Reassembler { 65*4185b066SAndroid Build Coastguard Worker /// Accumulate message data, possibly resulting in a complete message. accumulate<'a>(&mut self, frag: &'a [u8]) -> Option<Cow<'a, [u8]>>66*4185b066SAndroid Build Coastguard Worker pub fn accumulate<'a>(&mut self, frag: &'a [u8]) -> Option<Cow<'a, [u8]>> { 67*4185b066SAndroid Build Coastguard Worker let (more, content) = Self::split_msg(frag); 68*4185b066SAndroid Build Coastguard Worker if more { 69*4185b066SAndroid Build Coastguard Worker // More to come, so accumulate this data and return empty response. 70*4185b066SAndroid Build Coastguard Worker self.0.extend_from_slice(content); 71*4185b066SAndroid Build Coastguard Worker None 72*4185b066SAndroid Build Coastguard Worker } else if self.0.is_empty() { 73*4185b066SAndroid Build Coastguard Worker // For shorter messages (the mainline case) we can directly pass through the single 74*4185b066SAndroid Build Coastguard Worker // message's content. 75*4185b066SAndroid Build Coastguard Worker Some(Cow::Borrowed(content)) 76*4185b066SAndroid Build Coastguard Worker } else { 77*4185b066SAndroid Build Coastguard Worker // Process the accumulated full request as an owned vector 78*4185b066SAndroid Build Coastguard Worker let mut full_req = core::mem::take(&mut self.0); 79*4185b066SAndroid Build Coastguard Worker full_req.extend_from_slice(content); 80*4185b066SAndroid Build Coastguard Worker Some(Cow::Owned(full_req)) 81*4185b066SAndroid Build Coastguard Worker } 82*4185b066SAndroid Build Coastguard Worker } 83*4185b066SAndroid Build Coastguard Worker 84*4185b066SAndroid Build Coastguard Worker /// Split a message into an indication of whether more data is to come, and the content. 85*4185b066SAndroid Build Coastguard Worker /// 86*4185b066SAndroid Build Coastguard Worker /// # Panics 87*4185b066SAndroid Build Coastguard Worker /// 88*4185b066SAndroid Build Coastguard Worker /// This function panics if the provided message fragment has an unexpected message prefix. split_msg(data: &[u8]) -> (bool, &[u8])89*4185b066SAndroid Build Coastguard Worker fn split_msg(data: &[u8]) -> (bool, &[u8]) { 90*4185b066SAndroid Build Coastguard Worker if data.is_empty() { 91*4185b066SAndroid Build Coastguard Worker (false, data) 92*4185b066SAndroid Build Coastguard Worker } else { 93*4185b066SAndroid Build Coastguard Worker match data[0] { 94*4185b066SAndroid Build Coastguard Worker PREFIX_MORE_TO_COME => (true, &data[1..]), 95*4185b066SAndroid Build Coastguard Worker PREFIX_FINAL_FRAGMENT => (false, &data[1..]), 96*4185b066SAndroid Build Coastguard Worker _ => panic!("data fragment with incorrect prefix"), 97*4185b066SAndroid Build Coastguard Worker } 98*4185b066SAndroid Build Coastguard Worker } 99*4185b066SAndroid Build Coastguard Worker } 100*4185b066SAndroid Build Coastguard Worker } 101*4185b066SAndroid Build Coastguard Worker 102*4185b066SAndroid Build Coastguard Worker #[cfg(test)] 103*4185b066SAndroid Build Coastguard Worker mod tests { 104*4185b066SAndroid Build Coastguard Worker use super::*; 105*4185b066SAndroid Build Coastguard Worker use alloc::string::{String, ToString}; 106*4185b066SAndroid Build Coastguard Worker use alloc::vec; 107*4185b066SAndroid Build Coastguard Worker use core::cell::RefCell; 108*4185b066SAndroid Build Coastguard Worker 109*4185b066SAndroid Build Coastguard Worker #[test] test_fragmentation()110*4185b066SAndroid Build Coastguard Worker fn test_fragmentation() { 111*4185b066SAndroid Build Coastguard Worker let tests = [ 112*4185b066SAndroid Build Coastguard Worker ( 113*4185b066SAndroid Build Coastguard Worker "a0a1a2a3a4a5a6a7a8a9", 114*4185b066SAndroid Build Coastguard Worker 2, 115*4185b066SAndroid Build Coastguard Worker vec![ 116*4185b066SAndroid Build Coastguard Worker "cca0", "cca1", "cca2", "cca3", "cca4", "cca5", "cca6", "cca7", "cca8", "dda9", 117*4185b066SAndroid Build Coastguard Worker ], 118*4185b066SAndroid Build Coastguard Worker ), 119*4185b066SAndroid Build Coastguard Worker ("a0a1a2a3a4a5a6a7a8a9", 5, vec!["cca0a1a2a3", "cca4a5a6a7", "dda8a9"]), 120*4185b066SAndroid Build Coastguard Worker ("a0a1a2a3a4a5a6a7a8a9", 9, vec!["cca0a1a2a3a4a5a6a7", "dda8a9"]), 121*4185b066SAndroid Build Coastguard Worker ("a0a1a2a3a4a5a6a7a8a9", 80, vec!["dda0a1a2a3a4a5a6a7a8a9"]), 122*4185b066SAndroid Build Coastguard Worker ]; 123*4185b066SAndroid Build Coastguard Worker for (input, max_size, want) in &tests { 124*4185b066SAndroid Build Coastguard Worker let data = hex::decode(input).unwrap(); 125*4185b066SAndroid Build Coastguard Worker let fragmenter = Fragmenter::new(&data, *max_size); 126*4185b066SAndroid Build Coastguard Worker let got: Vec<String> = fragmenter.map(hex::encode).collect(); 127*4185b066SAndroid Build Coastguard Worker let want: Vec<String> = want.iter().map(|s| s.to_string()).collect(); 128*4185b066SAndroid Build Coastguard Worker assert_eq!(got, want, "for input {input} max_size {max_size}"); 129*4185b066SAndroid Build Coastguard Worker } 130*4185b066SAndroid Build Coastguard Worker } 131*4185b066SAndroid Build Coastguard Worker 132*4185b066SAndroid Build Coastguard Worker #[test] 133*4185b066SAndroid Build Coastguard Worker #[should_panic] test_reassembly_wrong_prefix()134*4185b066SAndroid Build Coastguard Worker fn test_reassembly_wrong_prefix() { 135*4185b066SAndroid Build Coastguard Worker // Failure case: unexpected marker byte 136*4185b066SAndroid Build Coastguard Worker let mut pending = Reassembler::default(); 137*4185b066SAndroid Build Coastguard Worker let _ = pending.accumulate(&[0x00, 0x01, 0x02, 0x03]); 138*4185b066SAndroid Build Coastguard Worker } 139*4185b066SAndroid Build Coastguard Worker 140*4185b066SAndroid Build Coastguard Worker #[test] test_reassembly()141*4185b066SAndroid Build Coastguard Worker fn test_reassembly() { 142*4185b066SAndroid Build Coastguard Worker let tests = [ 143*4185b066SAndroid Build Coastguard Worker // Single messages 144*4185b066SAndroid Build Coastguard Worker (vec!["dd"], ""), 145*4185b066SAndroid Build Coastguard Worker (vec!["dd0000"], "0000"), 146*4185b066SAndroid Build Coastguard Worker (vec!["dd010203"], "010203"), 147*4185b066SAndroid Build Coastguard Worker // Multipart messages. 148*4185b066SAndroid Build Coastguard Worker (vec!["cc0102", "dd0304"], "01020304"), 149*4185b066SAndroid Build Coastguard Worker (vec!["cc01", "cc02", "dd0304"], "01020304"), 150*4185b066SAndroid Build Coastguard Worker (vec!["cc", "cc02", "dd0304"], "020304"), 151*4185b066SAndroid Build Coastguard Worker // Failure case: empty message (no marker byte) 152*4185b066SAndroid Build Coastguard Worker (vec![], ""), 153*4185b066SAndroid Build Coastguard Worker ]; 154*4185b066SAndroid Build Coastguard Worker for (frags, want) in &tests { 155*4185b066SAndroid Build Coastguard Worker let mut done = false; 156*4185b066SAndroid Build Coastguard Worker let mut pending = Reassembler::default(); 157*4185b066SAndroid Build Coastguard Worker for frag in frags { 158*4185b066SAndroid Build Coastguard Worker assert!(!done, "left over fragments found"); 159*4185b066SAndroid Build Coastguard Worker let frag = hex::decode(frag).unwrap(); 160*4185b066SAndroid Build Coastguard Worker let result = pending.accumulate(&frag); 161*4185b066SAndroid Build Coastguard Worker if let Some(got) = result { 162*4185b066SAndroid Build Coastguard Worker assert_eq!(&hex::encode(got), want, "for input {frags:?}"); 163*4185b066SAndroid Build Coastguard Worker done = true; 164*4185b066SAndroid Build Coastguard Worker } 165*4185b066SAndroid Build Coastguard Worker } 166*4185b066SAndroid Build Coastguard Worker } 167*4185b066SAndroid Build Coastguard Worker } 168*4185b066SAndroid Build Coastguard Worker 169*4185b066SAndroid Build Coastguard Worker #[test] test_fragmentation_reassembly()170*4185b066SAndroid Build Coastguard Worker fn test_fragmentation_reassembly() { 171*4185b066SAndroid Build Coastguard Worker let input = "a0a1a2a3a4a5a6a7a8a9b0b1b2b3b4b5b6b7b8b9c0c1c2c3c4c5c6c7c8c9"; 172*4185b066SAndroid Build Coastguard Worker let data = hex::decode(input).unwrap(); 173*4185b066SAndroid Build Coastguard Worker for max_size in 2..data.len() + 2 { 174*4185b066SAndroid Build Coastguard Worker let fragmenter = Fragmenter::new(&data, max_size); 175*4185b066SAndroid Build Coastguard Worker let mut done = false; 176*4185b066SAndroid Build Coastguard Worker let mut pending = Reassembler::default(); 177*4185b066SAndroid Build Coastguard Worker for frag in fragmenter { 178*4185b066SAndroid Build Coastguard Worker assert!(!done, "left over fragments found"); 179*4185b066SAndroid Build Coastguard Worker let result = pending.accumulate(&frag); 180*4185b066SAndroid Build Coastguard Worker if let Some(got) = result { 181*4185b066SAndroid Build Coastguard Worker assert_eq!(&hex::encode(got), input, "for max_size {max_size}"); 182*4185b066SAndroid Build Coastguard Worker done = true; 183*4185b066SAndroid Build Coastguard Worker } 184*4185b066SAndroid Build Coastguard Worker } 185*4185b066SAndroid Build Coastguard Worker assert!(done); 186*4185b066SAndroid Build Coastguard Worker } 187*4185b066SAndroid Build Coastguard Worker } 188*4185b066SAndroid Build Coastguard Worker 189*4185b066SAndroid Build Coastguard Worker #[test] test_ta_fragmentation_wrapper()190*4185b066SAndroid Build Coastguard Worker fn test_ta_fragmentation_wrapper() { 191*4185b066SAndroid Build Coastguard Worker // Simulate a `send()` standalone function for responses. 192*4185b066SAndroid Build Coastguard Worker let rsp_reassembler = RefCell::new(Reassembler::default()); 193*4185b066SAndroid Build Coastguard Worker let full_rsp: RefCell<Option<Vec<u8>>> = RefCell::new(None); 194*4185b066SAndroid Build Coastguard Worker let send = |data: &[u8]| { 195*4185b066SAndroid Build Coastguard Worker if let Some(msg) = rsp_reassembler.borrow_mut().accumulate(data) { 196*4185b066SAndroid Build Coastguard Worker *full_rsp.borrow_mut() = Some(msg.to_vec()); 197*4185b066SAndroid Build Coastguard Worker } 198*4185b066SAndroid Build Coastguard Worker }; 199*4185b066SAndroid Build Coastguard Worker 200*4185b066SAndroid Build Coastguard Worker // Simulate a TA. 201*4185b066SAndroid Build Coastguard Worker struct Ta { 202*4185b066SAndroid Build Coastguard Worker pending_req: Reassembler, 203*4185b066SAndroid Build Coastguard Worker max_size: usize, 204*4185b066SAndroid Build Coastguard Worker } 205*4185b066SAndroid Build Coastguard Worker impl Ta { 206*4185b066SAndroid Build Coastguard Worker // Request fragment, response fragments emitted via `send`. 207*4185b066SAndroid Build Coastguard Worker fn process_fragment<S: Fn(&[u8])>(&mut self, req_frag: &[u8], send: S) { 208*4185b066SAndroid Build Coastguard Worker // Accumulate request fragments until able to feed complete request to `process`. 209*4185b066SAndroid Build Coastguard Worker if let Some(full_req) = self.pending_req.accumulate(req_frag) { 210*4185b066SAndroid Build Coastguard Worker // Full request message is available, invoke the callback to get a full 211*4185b066SAndroid Build Coastguard Worker // response. 212*4185b066SAndroid Build Coastguard Worker let full_rsp = self.process(&full_req); 213*4185b066SAndroid Build Coastguard Worker for rsp_frag in Fragmenter::new(&full_rsp, self.max_size) { 214*4185b066SAndroid Build Coastguard Worker send(&rsp_frag); 215*4185b066SAndroid Build Coastguard Worker } 216*4185b066SAndroid Build Coastguard Worker } 217*4185b066SAndroid Build Coastguard Worker } 218*4185b066SAndroid Build Coastguard Worker // Full request to full response. 219*4185b066SAndroid Build Coastguard Worker fn process(&self, req: &[u8]) -> Vec<u8> { 220*4185b066SAndroid Build Coastguard Worker // Simulate processing a request by echoing it back as a response. 221*4185b066SAndroid Build Coastguard Worker req.to_vec() 222*4185b066SAndroid Build Coastguard Worker } 223*4185b066SAndroid Build Coastguard Worker } 224*4185b066SAndroid Build Coastguard Worker 225*4185b066SAndroid Build Coastguard Worker let req = "a0a1a2a3a4a5a6a7a8a9b0b1b2b3b4b5b6b7b8b9c0c1c2c3c4c5c6c7c8c9"; 226*4185b066SAndroid Build Coastguard Worker let req_data = hex::decode(req).unwrap(); 227*4185b066SAndroid Build Coastguard Worker for max_size in 2..req_data.len() + 2 { 228*4185b066SAndroid Build Coastguard Worker let mut ta = Ta { pending_req: Default::default(), max_size }; 229*4185b066SAndroid Build Coastguard Worker // Simulate multiple fragmented messages arriving at the TA. 230*4185b066SAndroid Build Coastguard Worker for _msg_idx in 0..3 { 231*4185b066SAndroid Build Coastguard Worker // Reset the received-response buffer. 232*4185b066SAndroid Build Coastguard Worker *rsp_reassembler.borrow_mut() = Reassembler::default(); 233*4185b066SAndroid Build Coastguard Worker *full_rsp.borrow_mut() = None; 234*4185b066SAndroid Build Coastguard Worker 235*4185b066SAndroid Build Coastguard Worker for req_frag in Fragmenter::new(&req_data, max_size) { 236*4185b066SAndroid Build Coastguard Worker assert!(full_rsp.borrow().is_none(), "left over fragments found"); 237*4185b066SAndroid Build Coastguard Worker ta.process_fragment(&req_frag, send); 238*4185b066SAndroid Build Coastguard Worker } 239*4185b066SAndroid Build Coastguard Worker // After all the request fragments have been sent in, expect to have a complete 240*4185b066SAndroid Build Coastguard Worker // response. 241*4185b066SAndroid Build Coastguard Worker if let Some(rsp) = full_rsp.borrow().as_ref() { 242*4185b066SAndroid Build Coastguard Worker assert_eq!(hex::encode(rsp), req); 243*4185b066SAndroid Build Coastguard Worker } else { 244*4185b066SAndroid Build Coastguard Worker panic!("no response received"); 245*4185b066SAndroid Build Coastguard Worker } 246*4185b066SAndroid Build Coastguard Worker } 247*4185b066SAndroid Build Coastguard Worker } 248*4185b066SAndroid Build Coastguard Worker } 249*4185b066SAndroid Build Coastguard Worker } 250