diff --git a/src/error.rs b/src/error.rs index e7dc1f0..c30b83f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -57,8 +57,8 @@ pub enum Error { #[error("raw is too small for error cause")] ErrErrorCauseTooSmall, - #[error("unhandled ParamType")] - ErrParamTypeUnhandled, + #[error("unhandled ParamType: {typ}")] + ErrParamTypeUnhandled { typ: u16 }, #[error("unexpected ParamType")] ErrParamTypeUnexpected, diff --git a/src/param/mod.rs b/src/param/mod.rs index 50c23b8..13486ed 100644 --- a/src/param/mod.rs +++ b/src/param/mod.rs @@ -12,6 +12,7 @@ pub(crate) mod param_requested_hmac_algorithm; pub(crate) mod param_state_cookie; pub(crate) mod param_supported_extensions; pub(crate) mod param_type; +pub(crate) mod param_uknown; use crate::error::{Error, Result}; use crate::param::{ @@ -28,6 +29,8 @@ use param_type::*; use bytes::{Buf, Bytes, BytesMut}; use std::{any::Any, fmt}; +use self::param_uknown::ParamUnknown; + pub(crate) trait Param: fmt::Display + fmt::Debug { fn header(&self) -> ParamHeader; fn unmarshal(raw: &Bytes) -> Result @@ -57,7 +60,8 @@ pub(crate) fn build_param(raw_param: &Bytes) -> Result Ok(Box::new(ParamForwardTsnSupported::unmarshal(raw_param)?)), ParamType::SupportedExt => Ok(Box::new(ParamSupportedExtensions::unmarshal(raw_param)?)), @@ -68,6 +72,16 @@ pub(crate) fn build_param(raw_param: &Bytes) -> Result Ok(Box::new(ParamHeartbeatInfo::unmarshal(raw_param)?)), ParamType::OutSsnResetReq => Ok(Box::new(ParamOutgoingResetRequest::unmarshal(raw_param)?)), ParamType::ReconfigResp => Ok(Box::new(ParamReconfigResponse::unmarshal(raw_param)?)), - _ => Err(Error::ErrParamTypeUnhandled), + _ => { + // According to RFC https://datatracker.ietf.org/doc/html/rfc4960#section-3.2.1 + let stop_processing = ((raw_type >> 15) & 0x01) == 0; + if stop_processing { + Err(Error::ErrParamTypeUnhandled { typ: raw_type }) + } else { + // We still might need to report this param as unrecognized. + // This depends on the context though. + Ok(Box::new(ParamUnknown::unmarshal(raw_param)?)) + } + } } } diff --git a/src/param/param_header.rs b/src/param/param_header.rs index 24b3d2f..fc857e8 100644 --- a/src/param/param_header.rs +++ b/src/param/param_header.rs @@ -2,7 +2,7 @@ use super::{param_type::*, *}; use bytes::BufMut; -#[derive(Default, Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct ParamHeader { pub(crate) typ: ParamType, pub(crate) value_length: u16, @@ -43,7 +43,7 @@ impl Param for ParamHeader { } fn marshal_to(&self, writer: &mut BytesMut) -> Result { - writer.put_u16(self.typ as u16); + writer.put_u16(self.typ.into()); writer.put_u16(self.value_length + PARAM_HEADER_LENGTH as u16); Ok(writer.len()) } diff --git a/src/param/param_type.rs b/src/param/param_type.rs index a26e263..c1ee3fe 100644 --- a/src/param/param_type.rs +++ b/src/param/param_type.rs @@ -3,62 +3,62 @@ use std::fmt; /// paramType represents a SCTP INIT/INITACK parameter #[derive(Debug, Copy, Clone, PartialEq)] #[repr(C)] -#[derive(Default)] pub(crate) enum ParamType { - HeartbeatInfo = 1, + HeartbeatInfo, /// Heartbeat Info [RFCRFC4960] - Ipv4Addr = 5, + Ipv4Addr, /// IPv4 IP [RFCRFC4960] - Ipv6Addr = 6, + Ipv6Addr, /// IPv6 IP [RFCRFC4960] - StateCookie = 7, + StateCookie, /// State Cookie [RFCRFC4960] - UnrecognizedParam = 8, + UnrecognizedParam, /// Unrecognized Parameters [RFCRFC4960] - CookiePreservative = 9, + CookiePreservative, /// Cookie Preservative [RFCRFC4960] - HostNameAddr = 11, + HostNameAddr, /// Host Name IP [RFCRFC4960] - SupportedAddrTypes = 12, + SupportedAddrTypes, /// Supported IP Types [RFCRFC4960] - OutSsnResetReq = 13, + OutSsnResetReq, /// Outgoing SSN Reset Request Parameter [RFCRFC6525] - IncSsnResetReq = 14, + IncSsnResetReq, /// Incoming SSN Reset Request Parameter [RFCRFC6525] - SsnTsnResetReq = 15, + SsnTsnResetReq, /// SSN/TSN Reset Request Parameter [RFCRFC6525] - ReconfigResp = 16, + ReconfigResp, /// Re-configuration Response Parameter [RFCRFC6525] - AddOutStreamsReq = 17, + AddOutStreamsReq, /// Add Outgoing Streams Request Parameter [RFCRFC6525] - AddIncStreamsReq = 18, + AddIncStreamsReq, /// Add Incoming Streams Request Parameter [RFCRFC6525] - Random = 32770, + Random, /// Random (0x8002) [RFCRFC4805] - ChunkList = 32771, + ChunkList, /// Chunk List (0x8003) [RFCRFC4895] - ReqHmacAlgo = 32772, + ReqHmacAlgo, /// Requested HMAC Algorithm Parameter (0x8004) [RFCRFC4895] - Padding = 32773, + Padding, /// Padding (0x8005) - SupportedExt = 32776, + SupportedExt, /// Supported Extensions (0x8008) [RFCRFC5061] - ForwardTsnSupp = 49152, + ForwardTsnSupp, /// Forward TSN supported (0xC000) [RFCRFC3758] - AddIpAddr = 49153, + AddIpAddr, /// Add IP IP (0xC001) [RFCRFC5061] - DelIpaddr = 49154, + DelIpaddr, /// Delete IP IP (0xC002) [RFCRFC5061] - ErrClauseInd = 49155, + ErrClauseInd, /// Error Cause Indication (0xC003) [RFCRFC5061] - SetPriAddr = 49156, + SetPriAddr, /// Set Primary IP (0xC004) [RFCRFC5061] - SuccessInd = 49157, + SuccessInd, /// Success Indication (0xC005) [RFCRFC5061] - AdaptLayerInd = 49158, + AdaptLayerInd, /// Adaptation Layer Indication (0xC006) [RFCRFC5061] - #[default] - Unknown, + Unknown { + param_type: u16, + }, } impl fmt::Display for ParamType { @@ -124,7 +124,41 @@ impl From for ParamType { 49155 => ParamType::ErrClauseInd, 49156 => ParamType::SetPriAddr, 49157 => ParamType::SuccessInd, - _ => ParamType::Unknown, + _ => ParamType::Unknown { param_type: v }, + } + } +} + +impl From for u16 { + fn from(v: ParamType) -> u16 { + match v { + ParamType::HeartbeatInfo => 1, + ParamType::Ipv4Addr => 5, + ParamType::Ipv6Addr => 6, + ParamType::StateCookie => 7, + ParamType::UnrecognizedParam => 8, + ParamType::CookiePreservative => 9, + ParamType::HostNameAddr => 11, + ParamType::SupportedAddrTypes => 12, + ParamType::OutSsnResetReq => 13, + ParamType::IncSsnResetReq => 14, + ParamType::SsnTsnResetReq => 15, + ParamType::ReconfigResp => 16, + ParamType::AddOutStreamsReq => 17, + ParamType::AddIncStreamsReq => 18, + ParamType::Random => 32770, + ParamType::ChunkList => 32771, + ParamType::ReqHmacAlgo => 32772, + ParamType::Padding => 32773, + ParamType::SupportedExt => 32776, + ParamType::ForwardTsnSupp => 49152, + ParamType::AddIpAddr => 49153, + ParamType::DelIpaddr => 49154, + ParamType::ErrClauseInd => 49155, + ParamType::SetPriAddr => 49156, + ParamType::SuccessInd => 49157, + ParamType::AdaptLayerInd => 49158, + ParamType::Unknown { param_type, .. } => param_type, } } } diff --git a/src/param/param_uknown.rs b/src/param/param_uknown.rs new file mode 100644 index 0000000..028b381 --- /dev/null +++ b/src/param/param_uknown.rs @@ -0,0 +1,65 @@ +use std::any::Any; +use std::fmt::{Debug, Display, Formatter}; + +use bytes::{Bytes, BytesMut}; + +use crate::param::param_header::{ParamHeader, PARAM_HEADER_LENGTH}; +use crate::param::param_type::ParamType; +use crate::param::Param; + +/// This type is meant to represent ANY parameter for un/remarshaling purposes, where we do not have a more specific type for it. +/// This means we do not really understand the semantics of the param but can represent it. +/// +/// This is useful for usage in e.g.`ParamUnrecognized` where we want to report some unrecognized params back to the sender. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParamUnknown { + typ: u16, + value: Bytes, +} + +impl Display for ParamUnknown { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "ParamUnknown( {} {:?} )", self.header(), self.value) + } +} + +impl Param for ParamUnknown { + fn header(&self) -> ParamHeader { + ParamHeader { + typ: ParamType::Unknown { + param_type: self.typ, + }, + value_length: self.value.len() as u16, + } + } + + fn as_any(&self) -> &(dyn Any + Send + Sync) { + self + } + + fn unmarshal(raw: &Bytes) -> crate::error::Result + where + Self: Sized, + { + let header = ParamHeader::unmarshal(raw)?; + let value = raw.slice(PARAM_HEADER_LENGTH..PARAM_HEADER_LENGTH + header.value_length()); + Ok(Self { + typ: header.typ.into(), + value, + }) + } + + fn marshal_to(&self, buf: &mut BytesMut) -> crate::error::Result { + self.header().marshal_to(buf)?; + buf.extend(self.value.clone()); + Ok(buf.len()) + } + + fn value_length(&self) -> usize { + self.value.len() + } + + fn clone_to(&self) -> Box { + Box::new(self.clone()) + } +}