CFDP initial packet support #14
98
src/cfdp/lv.rs
Normal file
98
src/cfdp/lv.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use crate::cfdp::TlvLvError;
|
||||||
|
use crate::{ByteConversionError, SizeMissmatch};
|
||||||
|
use std::prelude::v1::String;
|
||||||
|
|
||||||
|
pub const MIN_LV_LEN: usize = 1;
|
||||||
|
|
||||||
|
/// Generic CFDP length-value (LV) abstraction.
|
||||||
|
///
|
||||||
|
/// This is just a thin wrapper around a raw slice which performs some additional error handling.
|
||||||
|
pub struct Lv<'a> {
|
||||||
|
data: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generic_len_check_data_serialization(
|
||||||
|
buf: &[u8],
|
||||||
|
data: &[u8],
|
||||||
|
min_overhead: usize,
|
||||||
|
) -> Result<(), ByteConversionError> {
|
||||||
|
if buf.len() < data.len() + min_overhead {
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: data.len() + min_overhead,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generic_len_check_deserialization(
|
||||||
|
buf: &[u8],
|
||||||
|
min_overheader: usize,
|
||||||
|
) -> Result<(), ByteConversionError> {
|
||||||
|
if buf.len() < min_overheader {
|
||||||
|
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: MIN_LV_LEN,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
impl<'a> Lv<'a> {
|
||||||
|
pub fn new(data: &[u8]) -> Result<Lv, TlvLvError> {
|
||||||
|
if data.len() > u8::MAX as usize {
|
||||||
|
return Err(TlvLvError::DataTooLarge(data.len()));
|
||||||
|
}
|
||||||
|
Ok(Lv { data })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to build a string LV. This is especially useful for the file or directory
|
||||||
|
/// path LVs
|
||||||
|
pub fn new_from_str(str_slice: &str) -> Result<Lv, TlvLvError> {
|
||||||
|
Self::new(str_slice.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to build a string LV. This is especially useful for the file or directory
|
||||||
|
/// path LVs
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn new_from_string(string: &'a String) -> Result<Lv<'a>, TlvLvError> {
|
||||||
|
Self::new(string.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> &[u8] {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the LV to a raw buffer. Please note that the first byte will contain the length
|
||||||
|
/// of the value, but the values may not exceed a length of [u8::MAX].
|
||||||
|
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||||
|
generic_len_check_data_serialization(buf, self.data, MIN_LV_LEN)?;
|
||||||
|
Ok(self.write_to_be_bytes_no_len_check(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a LV from a raw buffer.
|
||||||
|
pub fn from_be_bytes(buf: &'a [u8]) -> Result<Lv<'a>, TlvLvError> {
|
||||||
|
generic_len_check_deserialization(buf, MIN_LV_LEN)?;
|
||||||
|
Self::from_be_bytes_no_len_check(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_to_be_bytes_no_len_check(&self, buf: &mut [u8]) -> usize {
|
||||||
|
// Length check in constructor ensures the length always has a valid value.
|
||||||
|
buf[0] = self.data.len() as u8;
|
||||||
|
buf[MIN_LV_LEN..self.data.len() + MIN_LV_LEN].copy_from_slice(self.data);
|
||||||
|
MIN_LV_LEN + self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_be_bytes_no_len_check(buf: &'a [u8]) -> Result<Lv<'a>, TlvLvError> {
|
||||||
|
let value_len = buf[0] as usize;
|
||||||
|
if buf.len() < value_len + MIN_LV_LEN {
|
||||||
|
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: MIN_LV_LEN + value_len,
|
||||||
|
})
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
data: &buf[MIN_LV_LEN..MIN_LV_LEN + value_len],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,12 @@
|
|||||||
|
use crate::ByteConversionError;
|
||||||
|
use core::fmt::{Display, Formatter};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub mod lv;
|
||||||
pub mod pdu;
|
pub mod pdu;
|
||||||
pub mod tlv;
|
pub mod tlv;
|
||||||
|
|
||||||
@ -127,3 +132,49 @@ pub enum ChecksumType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4];
|
pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4];
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub enum TlvLvError {
|
||||||
|
DataTooLarge(usize),
|
||||||
|
ByteConversionError(ByteConversionError),
|
||||||
|
/// Only relevant for TLV de-serialization.
|
||||||
|
UnknownTlvType(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ByteConversionError> for TlvLvError {
|
||||||
|
fn from(value: ByteConversionError) -> Self {
|
||||||
|
Self::ByteConversionError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TlvLvError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
TlvLvError::UnknownTlvType(raw_tlv_id) => {
|
||||||
|
write!(f, "unknown TLV type {raw_tlv_id}")
|
||||||
|
}
|
||||||
|
TlvLvError::DataTooLarge(data_len) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"data with size {} larger than allowed {} bytes",
|
||||||
|
data_len,
|
||||||
|
u8::MAX
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TlvLvError::ByteConversionError(e) => {
|
||||||
|
write!(f, "{}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl Error for TlvLvError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
TlvLvError::ByteConversionError(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
|
use crate::cfdp::lv::Lv;
|
||||||
use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
|
use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
|
||||||
use crate::cfdp::ChecksumType;
|
use crate::cfdp::ChecksumType;
|
||||||
|
|
||||||
pub struct MetadataParams {
|
pub struct MetadataGenericParams {
|
||||||
closure_requested: bool,
|
closure_requested: bool,
|
||||||
checksum_type: ChecksumType,
|
checksum_type: ChecksumType,
|
||||||
file_size: u64,
|
file_size: u64,
|
||||||
//src_file_name:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MetadataPdu {
|
pub struct MetadataPdu<'src_name, 'dest_name> {
|
||||||
pdu_header: PduHeader,
|
pdu_header: PduHeader,
|
||||||
file_directive: FileDirectiveType,
|
file_directive: FileDirectiveType,
|
||||||
metadata_params: MetadataParams,
|
metadata_params: MetadataGenericParams,
|
||||||
|
src_file_name: Option<Lv<'src_name>>,
|
||||||
|
dest_file_name: Option<Lv<'dest_name>>,
|
||||||
}
|
}
|
||||||
|
@ -63,14 +63,21 @@ impl Display for PduError {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
PduError::ByteConversionError(e) => {
|
PduError::ByteConversionError(e) => {
|
||||||
write!(f, "low level byte conversion error: {e}")
|
write!(f, "{}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl Error for PduError {}
|
impl Error for PduError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
PduError::ByteConversionError(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ByteConversionError> for PduError {
|
impl From<ByteConversionError> for PduError {
|
||||||
fn from(value: ByteConversionError) -> Self {
|
fn from(value: ByteConversionError) -> Self {
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
use crate::{ByteConversionError, SizeMissmatch};
|
use crate::cfdp::lv::{
|
||||||
|
generic_len_check_data_serialization, generic_len_check_deserialization, Lv, MIN_LV_LEN,
|
||||||
|
};
|
||||||
|
use crate::cfdp::TlvLvError;
|
||||||
|
use crate::ByteConversionError;
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -19,68 +23,40 @@ pub enum TlvType {
|
|||||||
|
|
||||||
pub struct Tlv<'a> {
|
pub struct Tlv<'a> {
|
||||||
tlv_type: TlvType,
|
tlv_type: TlvType,
|
||||||
data: &'a [u8],
|
lv: Lv<'a>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub enum TlvError {
|
|
||||||
DataTooLarge(usize),
|
|
||||||
ByteConversionError(ByteConversionError),
|
|
||||||
UnknownTlvType(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ByteConversionError> for TlvError {
|
|
||||||
fn from(value: ByteConversionError) -> Self {
|
|
||||||
Self::ByteConversionError(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Tlv<'a> {
|
impl<'a> Tlv<'a> {
|
||||||
pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv, TlvError> {
|
pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv, TlvLvError> {
|
||||||
if data.len() > u8::MAX as usize {
|
if data.len() > u8::MAX as usize {
|
||||||
return Err(TlvError::DataTooLarge(data.len()));
|
return Err(TlvLvError::DataTooLarge(data.len()));
|
||||||
}
|
}
|
||||||
Ok(Tlv { tlv_type, data })
|
Ok(Tlv {
|
||||||
|
tlv_type,
|
||||||
|
lv: Lv::new(data)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||||
if buf.len() < self.data.len() + MIN_TLV_LEN {
|
generic_len_check_data_serialization(buf, self.value(), MIN_TLV_LEN)?;
|
||||||
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
|
||||||
found: buf.len(),
|
|
||||||
expected: self.data.len() + MIN_TLV_LEN,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
buf[0] = self.tlv_type as u8;
|
buf[0] = self.tlv_type as u8;
|
||||||
// Length check in constructor ensures the length always has a valid value.
|
self.lv.write_to_be_bytes_no_len_check(&mut buf[1..]);
|
||||||
buf[1] = self.data.len() as u8;
|
Ok(MIN_TLV_LEN + self.value().len())
|
||||||
buf[MIN_TLV_LEN..self.data.len() + MIN_TLV_LEN].copy_from_slice(self.data);
|
|
||||||
Ok(MIN_TLV_LEN + self.data.len())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_be_bytes(buf: &'a [u8]) -> Result<Tlv<'a>, TlvError> {
|
pub fn value(&self) -> &[u8] {
|
||||||
if buf.len() < MIN_TLV_LEN {
|
self.lv.value()
|
||||||
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
|
||||||
found: buf.len(),
|
|
||||||
expected: MIN_TLV_LEN,
|
|
||||||
})
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_be_bytes(buf: &'a [u8]) -> Result<Tlv<'a>, TlvLvError> {
|
||||||
|
generic_len_check_deserialization(buf, MIN_TLV_LEN)?;
|
||||||
let tlv_type_res = TlvType::try_from(buf[0]);
|
let tlv_type_res = TlvType::try_from(buf[0]);
|
||||||
if tlv_type_res.is_err() {
|
if tlv_type_res.is_err() {
|
||||||
return Err(TlvError::UnknownTlvType(buf[1]));
|
return Err(TlvLvError::UnknownTlvType(buf[1]));
|
||||||
}
|
|
||||||
let value_len = buf[1] as usize;
|
|
||||||
if buf.len() < value_len {
|
|
||||||
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
|
||||||
found: buf.len(),
|
|
||||||
expected: MIN_TLV_LEN + value_len,
|
|
||||||
})
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
tlv_type: tlv_type_res.unwrap(),
|
tlv_type: tlv_type_res.unwrap(),
|
||||||
data: &buf[MIN_TLV_LEN..MIN_TLV_LEN + value_len],
|
lv: Lv::from_be_bytes(&buf[MIN_LV_LEN..])?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user