diff --git a/rtc-sctp/src/param/mod.rs b/rtc-sctp/src/param/mod.rs index b538d6f..8d89c52 100644 --- a/rtc-sctp/src/param/mod.rs +++ b/rtc-sctp/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::param::{ param_chunk_list::ParamChunkList, param_forward_tsn_supported::ParamForwardTsnSupported, @@ -20,6 +21,7 @@ use crate::param::{ param_reconfig_response::ParamReconfigResponse, param_requested_hmac_algorithm::ParamRequestedHmacAlgorithm, param_state_cookie::ParamStateCookie, param_supported_extensions::ParamSupportedExtensions, + param_uknown::ParamUnknown, }; use param_header::*; use param_type::*; @@ -57,7 +59,8 @@ pub(crate) fn build_param(raw_param: &Bytes) -> Result> { return Err(Error::ErrParamHeaderTooShort); } let reader = &mut raw_param.slice(..2); - let t: ParamType = reader.get_u16().into(); + let raw_type = reader.get_u16(); + let t: ParamType = raw_type.into(); match t { ParamType::ForwardTsnSupp => Ok(Box::new(ParamForwardTsnSupported::unmarshal(raw_param)?)), ParamType::SupportedExt => Ok(Box::new(ParamSupportedExtensions::unmarshal(raw_param)?)), @@ -68,6 +71,16 @@ pub(crate) fn build_param(raw_param: &Bytes) -> Result> { ParamType::HeartbeatInfo => 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/rtc-sctp/src/param/param_header.rs b/rtc-sctp/src/param/param_header.rs index d3d8b71..89f9dd9 100644 --- a/rtc-sctp/src/param/param_header.rs +++ b/rtc-sctp/src/param/param_header.rs @@ -3,7 +3,7 @@ use super::{param_type::*, *}; use bytes::{Buf, BufMut, Bytes, BytesMut}; use std::fmt; -#[derive(Default, Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct ParamHeader { pub(crate) typ: ParamType, pub(crate) value_length: u16, @@ -44,7 +44,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/rtc-sctp/src/param/param_type.rs b/rtc-sctp/src/param/param_type.rs index 25c4af9..c1ee3fe 100644 --- a/rtc-sctp/src/param/param_type.rs +++ b/rtc-sctp/src/param/param_type.rs @@ -1,63 +1,64 @@ use std::fmt; /// paramType represents a SCTP INIT/INITACK parameter -#[derive(Default, Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq)] #[repr(C)] 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 { @@ -123,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/rtc-sctp/src/param/param_uknown.rs b/rtc-sctp/src/param/param_uknown.rs new file mode 100644 index 0000000..2338cf5 --- /dev/null +++ b/rtc-sctp/src/param/param_uknown.rs @@ -0,0 +1,66 @@ +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; +use shared::error::Result; + +/// 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) { + self + } + + fn unmarshal(raw: &Bytes) -> 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) -> 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()) + } +} diff --git a/rtc-shared/src/error.rs b/rtc-shared/src/error.rs index 43026ee..b7e7577 100644 --- a/rtc-shared/src/error.rs +++ b/rtc-shared/src/error.rs @@ -928,8 +928,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, @@ -1095,7 +1095,7 @@ pub enum Error { //Data Channel #[error( - "DataChannel message is not long enough to determine type: (expected: {expected}, actual: {actual})" + "DataChannel message is not long enough to determine type: (expected: {expected}, actual: {actual})" )] UnexpectedEndOfBuffer { expected: usize, actual: usize }, #[error("Unknown MessageType {0}")] @@ -1309,7 +1309,8 @@ pub enum Error { /// ErrRegisterHeaderExtensionNoFreeID indicates that there was no extension ID available which /// in turn means that all 15 available id(1 through 14) have been used. - #[error("no header extension ID was free to use(this means the maximum of 15 extensions have been registered)")] + #[error("no header extension ID was free to use(this means the maximum of 15 extensions have been registered)" + )] ErrRegisterHeaderExtensionNoFreeID, /// ErrSimulcastProbeOverflow indicates that too many Simulcast probe streams are in flight and the requested SSRC was ignored @@ -1478,7 +1479,8 @@ pub enum Error { SdpEmptyTimeDescription, #[error("parse extmap: {0}")] ParseExtMap(String), - #[error("{} --> {} <-- {}", .s.substring(0,*.p), .s.substring(*.p, *.p+1), .s.substring(*.p+1, .s.len()))] + #[error("{} --> {} <-- {}", .s.substring(0,*.p), .s.substring(*.p, *.p+1), .s.substring(*.p+1, .s.len()) + )] SyntaxError { s: String, p: usize }, //Third Party Error