diff --git a/src/ecss/tm_pus_a.rs b/src/ecss/tm_pus_a.rs index 81266fc..f20d36c 100644 --- a/src/ecss/tm_pus_a.rs +++ b/src/ecss/tm_pus_a.rs @@ -1132,8 +1132,6 @@ mod tests { use super::*; use crate::time::cds::CdsTime; - #[cfg(feature = "serde")] - use crate::time::CcsdsTimeProvider; use crate::SpHeader; use crate::{ecss::PusVersion::PusA, util::UnsignedByteFieldU16}; #[cfg(feature = "serde")] diff --git a/src/uslp/mod.rs b/src/uslp/mod.rs index 86fc4f9..2e8b803 100644 --- a/src/uslp/mod.rs +++ b/src/uslp/mod.rs @@ -129,12 +129,12 @@ impl PrimaryHeader { Ok(()) } - #[inline(always)] + #[inline] pub fn vc_frame_count(&self) -> u64 { self.vc_frame_count } - #[inline(always)] + #[inline] pub fn vc_frame_count_len(&self) -> u8 { self.vc_frame_count_len } @@ -328,6 +328,7 @@ impl ConstructionRule { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TransferFrameDataFieldHeader { /// Construction rule for the TFDZ. construction_rule: ConstructionRule, @@ -391,6 +392,7 @@ impl TransferFrameDataFieldHeader { /// Simple USLP transfer frame reader. /// /// Currently, only insert zone lengths of 0 are supported. +#[derive(Debug)] pub struct TransferFrameReader<'buf> { primary_header: PrimaryHeader, data_field_header: TransferFrameDataFieldHeader, @@ -430,12 +432,14 @@ impl<'buf> TransferFrameReader<'buf> { )); } let data_end = data_idx + data_len; - let mut digest = CRC_CCITT_FALSE.digest(); - digest.update(&buf[0..frame_len]); - if digest.finalize() != 0 { - return Err(UslpError::ChecksumFailure(u16::from_be_bytes( - buf[frame_len - 2..frame_len].try_into().unwrap(), - ))); + if has_fecf { + let mut digest = CRC_CCITT_FALSE.digest(); + digest.update(&buf[0..frame_len]); + if digest.finalize() != 0 { + return Err(UslpError::ChecksumFailure(u16::from_be_bytes( + buf[frame_len - 2..frame_len].try_into().unwrap(), + ))); + } } Ok(Self { primary_header, @@ -468,8 +472,6 @@ impl<'buf> TransferFrameReader<'buf> { #[cfg(test)] mod tests { - use std::println; - use super::*; fn common_basic_check(buf: &[u8]) { @@ -508,6 +510,8 @@ mod tests { // Virtual channel count 0. assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 7); common_basic_check(&buf); + assert_eq!(primary_header.vc_frame_count_len(), 0); + assert_eq!(primary_header.vc_frame_count(), 0); // Bypass / Sequence Control Flag. assert_eq!( (buf[6] >> 7) & 0b1, @@ -544,6 +548,8 @@ mod tests { primary_header.set_vc_frame_count(4, 0x12345678).unwrap(); // Virtual channel count 4. assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 11); + assert_eq!(primary_header.vc_frame_count_len(), 4); + assert_eq!(primary_header.vc_frame_count(), 0x12345678); common_basic_check(&buf); // Bypass / Sequence Control Flag. assert_eq!( @@ -565,6 +571,31 @@ mod tests { ); } + #[test] + fn test_vcf_count_len_two() { + let mut buf: [u8; 16] = [0; 16]; + // Should be all zeros after writing. + buf[6] = 0xff; + let mut primary_header = PrimaryHeader::new( + 0b10100101_11000011, + SourceOrDestField::Dest, + 0b110101, + 0b1010, + 0x2345, + ) + .unwrap(); + primary_header.set_vc_frame_count(2, 5).unwrap(); + assert_eq!(primary_header.vc_frame_count_len(), 2); + assert_eq!(primary_header.vc_frame_count(), 5); + assert_eq!(primary_header.write_to_be_bytes(&mut buf).unwrap(), 9); + assert_eq!(buf[6] & 0b111, 2); + assert_eq!(u16::from_be_bytes(buf[7..9].try_into().unwrap()), 5); + + let primary_header = PrimaryHeader::from_bytes(&buf).unwrap(); + assert_eq!(primary_header.vc_frame_count_len(), 2); + assert_eq!(primary_header.vc_frame_count(), 5); + } + #[test] fn test_reading_0() { let mut buf: [u8; 8] = [0; 8]; @@ -667,20 +698,146 @@ mod tests { let mut digest = CRC_CCITT_FALSE.digest(); digest.update(&buf[0..header_len + 2]); buf[header_len + 2..header_len + 4].copy_from_slice(&digest.finalize().to_be_bytes()); - println!("Buffer: {:x?}", buf); // Now parse the frame. let frame = TransferFrameReader::from_bytes(&buf, true).unwrap(); + assert_eq!(*frame.primary_header(), primary_header); assert_eq!(frame.data().len(), 1); assert_eq!(frame.data()[0], 0x42); assert_eq!( - frame.data_field_header().uslp_protocol_id, + frame.data_field_header().uslp_protocol_id(), UslpProtocolId::UserDefinedOctetStream ); assert_eq!( - frame.data_field_header().construction_rule, + frame.data_field_header().construction_rule(), ConstructionRule::NoSegmentation ); assert!(frame.data_field_header().fhp_or_lvo().is_none()); assert_eq!(frame.len_frame(), 11); + assert!(frame.operational_control_field().is_none()); + } + + #[test] + fn test_frame_parser_invalid_checksum() { + let mut buf: [u8; 32] = [0; 32]; + // Build a variable frame manually. + let mut primary_header = + PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); + let header_len = primary_header.len_header(); + buf[header_len] = ((ConstructionRule::NoSegmentation as u8) << 5) + | (UslpProtocolId::UserDefinedOctetStream as u8) & 0b11111; + buf[header_len + 1] = 0x42; + // 1 byte TFDH, 1 byte data, 2 bytes CRC. + primary_header.set_frame_len(header_len + 4); + primary_header.write_to_be_bytes(&mut buf).unwrap(); + // Now parse the frame without having calculated the checksum. + match TransferFrameReader::from_bytes(&buf, true) { + Ok(_) => panic!("transfer frame read call did not fail"), + Err(e) => { + assert_eq!(e, UslpError::ChecksumFailure(0)); + } + } + } + + #[test] + fn test_frame_parser_buf_too_small() { + let mut buf: [u8; 32] = [0; 32]; + // Build a variable frame manually. + let mut primary_header = + PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); + let header_len = primary_header.len_header(); + buf[header_len] = ((ConstructionRule::NoSegmentation as u8) << 5) + | (UslpProtocolId::UserDefinedOctetStream as u8) & 0b11111; + buf[header_len + 1] = 0x42; + // 1 byte TFDH, 1 byte data, 2 bytes CRC. + primary_header.set_frame_len(header_len + 4); + primary_header.write_to_be_bytes(&mut buf).unwrap(); + // Now parse the frame. + let error = TransferFrameReader::from_bytes(&buf[0..7], true).unwrap_err(); + assert_eq!( + error, + ByteConversionError::FromSliceTooSmall { + expected: primary_header.len_frame(), + found: 7 + } + .into() + ); + } + + #[test] + fn test_from_bytes_too_small_0() { + let buf: [u8; 3] = [0; 3]; + assert_eq!( + PrimaryHeader::from_bytes(&buf).unwrap_err(), + ByteConversionError::FromSliceTooSmall { + found: 3, + expected: 4 + } + .into() + ); + } + + #[test] + fn test_from_bytes_too_small_1() { + let buf: [u8; 6] = [0; 6]; + assert_eq!( + PrimaryHeader::from_bytes(&buf).unwrap_err(), + ByteConversionError::FromSliceTooSmall { + found: 6, + expected: 7 + } + .into() + ); + } + + #[test] + fn test_from_bytes_truncated_not_supported() { + let mut buf: [u8; 7] = [0; 7]; + let primary_header = + PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); + primary_header.write_to_be_bytes(&mut buf).unwrap(); + // Set truncated header flag manually. + buf[3] |= 0b1; + assert_eq!( + PrimaryHeader::from_bytes(&buf).unwrap_err(), + UslpError::HeaderIsTruncated + ); + } + + #[test] + fn test_from_bytes_too_small_2() { + let mut buf: [u8; 16] = [0; 16]; + // Should be all zeros after writing. + buf[6] = 0xff; + let mut primary_header = PrimaryHeader::new( + 0b10100101_11000011, + SourceOrDestField::Dest, + 0b110101, + 0b1010, + 0x2345, + ) + .unwrap(); + primary_header.set_vc_frame_count(4, 0x12345678).unwrap(); + primary_header.write_to_be_bytes(&mut buf).unwrap(); + + assert_eq!( + PrimaryHeader::from_bytes(&buf[0..8]).unwrap_err(), + UslpError::ByteConversion(ByteConversionError::FromSliceTooSmall { + found: 8, + expected: 11 + }) + ); + } + + #[test] + fn test_invalid_version_number() { + let mut buf: [u8; 7] = [0; 7]; + let primary_header = + PrimaryHeader::new(0x01, SourceOrDestField::Dest, 0b110101, 0b1010, 0).unwrap(); + primary_header.write_to_be_bytes(&mut buf).unwrap(); + buf[0] &= 0b00001111; + assert_eq!( + PrimaryHeader::from_bytes(&buf).unwrap_err(), + UslpError::InvalidVersionNumber(0) + ); } }