use super::{chunk_header::*, chunk_type::*, *}; use crate::param::param_type::ParamType; use crate::param::{param_header::*, *}; use crate::util::get_padding_size; ///https://tools.ietf.org/html/rfc6525#section-3.1 ///chunkReconfig represents an SCTP Chunk used to reconfigure streams. /// /// 0 1 2 3 /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ///| Type = 130 | Chunk Flags | Chunk Length | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ///| | ///| Re-configuration Parameter | ///| | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ///| | ///| Re-configuration Parameter (optional) | ///| | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ #[derive(Default, Debug)] pub(crate) struct ChunkReconfig { pub(crate) param_a: Option>, pub(crate) param_b: Option>, } impl Clone for ChunkReconfig { fn clone(&self) -> Self { ChunkReconfig { param_a: self.param_a.as_ref().cloned(), param_b: self.param_b.as_ref().cloned(), } } } /// makes chunkReconfig printable impl fmt::Display for ChunkReconfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut res = String::new(); if let Some(param_a) = &self.param_a { res += format!("Param A:\n {}", param_a).as_str(); } if let Some(param_b) = &self.param_b { res += format!("Param B:\n {}", param_b).as_str() } write!(f, "{}", res) } } impl Chunk for ChunkReconfig { fn header(&self) -> ChunkHeader { ChunkHeader { typ: CT_RECONFIG, flags: 0, value_length: self.value_length() as u16, } } fn unmarshal(raw: &Bytes) -> Result { let header = ChunkHeader::unmarshal(raw)?; if header.typ != CT_RECONFIG { return Err(Error::ErrChunkTypeNotReconfig); } if header.value_length() < PARAM_HEADER_LENGTH { return Err(Error::ErrChunkTooShort); } let param_a = build_param(&raw.slice(CHUNK_HEADER_SIZE..CHUNK_HEADER_SIZE + header.value_length()))?; let padding = get_padding_size(PARAM_HEADER_LENGTH + param_a.value_length()); let offset = CHUNK_HEADER_SIZE + PARAM_HEADER_LENGTH + param_a.value_length() + padding; let param_b = if CHUNK_HEADER_SIZE + header.value_length() > offset { Some(build_param( &raw.slice(offset..CHUNK_HEADER_SIZE + header.value_length()), )?) } else { None }; Ok(ChunkReconfig { param_a: Some(param_a), param_b, }) } fn marshal_to(&self, writer: &mut BytesMut) -> Result { self.header().marshal_to(writer)?; let param_a_value_length = if let Some(param_a) = &self.param_a { writer.extend(param_a.marshal()?); param_a.value_length() } else { return Err(Error::ErrChunkReconfigInvalidParamA); }; if let Some(param_b) = &self.param_b { // Pad param A let padding = get_padding_size(PARAM_HEADER_LENGTH + param_a_value_length); writer.extend(vec![0u8; padding]); writer.extend(param_b.marshal()?); } Ok(writer.len()) } fn check(&self) -> Result<()> { let param_a = self .param_a .as_ref() .ok_or(Error::ErrChunkReconfigInvalidParamA)?; let Some(param_b) = &self.param_b else { return Ok(()); }; let combination = (param_a.header().typ, param_b.header().typ); if matches!( combination, (ParamType::OutSsnResetReq, ParamType::IncSsnResetReq) | (ParamType::AddOutStreamsReq, ParamType::AddIncStreamsReq) | (ParamType::ReconfigResp, ParamType::OutSsnResetReq) | (ParamType::ReconfigResp, ParamType::ReconfigResp) ) { Ok(()) } else { Err(Error::ErrChunkReconfigInvalidParamCombination) } } fn value_length(&self) -> usize { let mut l = PARAM_HEADER_LENGTH; let param_a_value_length = if let Some(param_a) = &self.param_a { l += param_a.value_length(); param_a.value_length() } else { 0 }; if let Some(param_b) = &self.param_b { let padding = get_padding_size(PARAM_HEADER_LENGTH + param_a_value_length); l += PARAM_HEADER_LENGTH + param_b.value_length() + padding; } l } fn as_any(&self) -> &(dyn Any + Send + Sync) { self } }