use crate::math::round_up_to_4;
use crate::packet::AsSerializableTlv;
use crate::packet::ChunkParseError;
use crate::packet::SerializableTlv;
use crate::packet::TLV_HEADER_SIZE;
use crate::packet::ensure;
use crate::packet::forward_tsn_supported_parameter::ForwardTsnSupportedParameter;
use crate::packet::forward_tsn_supported_parameter::{self};
use crate::packet::heartbeat_info_parameter::HeartbeatInfoParameter;
use crate::packet::heartbeat_info_parameter::{self};
use crate::packet::incoming_ssn_reset_request_parameter::IncomingSsnResetRequestParameter;
use crate::packet::incoming_ssn_reset_request_parameter::{self};
use crate::packet::outgoing_ssn_reset_request_parameter::OutgoingSsnResetRequestParameter;
use crate::packet::outgoing_ssn_reset_request_parameter::{self};
use crate::packet::read_u16_be;
use crate::packet::reconfiguration_response_parameter::ReconfigurationResponseParameter;
use crate::packet::reconfiguration_response_parameter::{self};
use crate::packet::state_cookie_parameter::StateCookieParameter;
use crate::packet::state_cookie_parameter::{self};
use crate::packet::supported_extensions_parameter::SupportedExtensionsParameter;
use crate::packet::supported_extensions_parameter::{self};
use crate::packet::unknown_parameter::UnknownParameter;
use crate::packet::write_u16_be;
use crate::packet::zero_checksum_acceptable_parameter::ZeroChecksumAcceptableParameter;
use crate::packet::zero_checksum_acceptable_parameter::{self};
use std::cmp;
pub(crate) const PARAMETER_HEADER_SIZE: usize = 4;
#[derive(Debug)]
pub(crate) struct RawParameter<'a> {
pub(crate) typ: u16,
pub(crate) value: &'a [u8],
}
impl<'a> RawParameter<'a> {
pub(crate) fn from_bytes(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), ChunkParseError> {
ensure!(bytes.len() >= PARAMETER_HEADER_SIZE, ChunkParseError::InvalidLength);
let typ = read_u16_be!(&bytes[0..2]);
let length = read_u16_be!(&bytes[2..4]) as usize;
ensure!(length >= TLV_HEADER_SIZE && length <= bytes.len(), ChunkParseError::InvalidLength);
let padded_length = round_up_to_4!(length);
let end_offset = cmp::min(padded_length, bytes.len());
Ok((Self { typ, value: &bytes[PARAMETER_HEADER_SIZE..length] }, &bytes[end_offset..]))
}
}
#[inline]
pub fn write_parameter_header(typ: u16, value_size: usize, output: &mut [u8]) -> &mut [u8] {
let serialized_size = PARAMETER_HEADER_SIZE + value_size;
assert!(output.len() >= serialized_size);
write_u16_be!(&mut output[0..2], typ);
write_u16_be!(&mut output[2..4], serialized_size as u16);
&mut output[PARAMETER_HEADER_SIZE..serialized_size]
}
pub const PARAMETER_TYPE_SSN_TSN_RESET_REQUEST: u16 = 15;
pub const PARAMETER_TYPE_ADD_OUTGOING_STREAMS_REQUEST: u16 = 17;
pub const PARAMETER_TYPE_ADD_INCOMING_STREAMS_REQUEST: u16 = 18;
#[derive(Debug)]
pub enum Parameter {
HeartbeatInfo(HeartbeatInfoParameter),
StateCookie(StateCookieParameter),
OutgoingSsnResetRequest(OutgoingSsnResetRequestParameter),
IncomingSsnResetRequest(IncomingSsnResetRequestParameter),
SsnTsnResetRequest(UnknownParameter),
ReconfigurationResponse(ReconfigurationResponseParameter),
AddOutgoingStreamsRequest(UnknownParameter),
AddIncomingStreamsRequest(UnknownParameter),
SupportedExtensions(SupportedExtensionsParameter),
ForwardTsnSupported(ForwardTsnSupportedParameter),
ZeroChecksumAcceptable(ZeroChecksumAcceptableParameter),
Unknown(UnknownParameter),
}
impl TryFrom<RawParameter<'_>> for Parameter {
type Error = ChunkParseError;
fn try_from(raw: RawParameter<'_>) -> Result<Self, ChunkParseError> {
match raw.typ {
heartbeat_info_parameter::PARAMETER_TYPE => {
HeartbeatInfoParameter::try_from(raw).map(Parameter::HeartbeatInfo)
}
state_cookie_parameter::PARAMETER_TYPE => {
StateCookieParameter::try_from(raw).map(Parameter::StateCookie)
}
supported_extensions_parameter::PARAMETER_TYPE => {
SupportedExtensionsParameter::try_from(raw).map(Parameter::SupportedExtensions)
}
zero_checksum_acceptable_parameter::PARAMETER_TYPE => {
ZeroChecksumAcceptableParameter::try_from(raw)
.map(Parameter::ZeroChecksumAcceptable)
}
forward_tsn_supported_parameter::PARAMETER_TYPE => {
ForwardTsnSupportedParameter::try_from(raw).map(Parameter::ForwardTsnSupported)
}
outgoing_ssn_reset_request_parameter::PARAMETER_TYPE => {
OutgoingSsnResetRequestParameter::try_from(raw)
.map(Parameter::OutgoingSsnResetRequest)
}
incoming_ssn_reset_request_parameter::PARAMETER_TYPE => {
IncomingSsnResetRequestParameter::try_from(raw)
.map(Parameter::IncomingSsnResetRequest)
}
PARAMETER_TYPE_SSN_TSN_RESET_REQUEST => {
UnknownParameter::try_from(raw).map(Parameter::SsnTsnResetRequest)
}
reconfiguration_response_parameter::PARAMETER_TYPE => {
ReconfigurationResponseParameter::try_from(raw)
.map(Parameter::ReconfigurationResponse)
}
PARAMETER_TYPE_ADD_OUTGOING_STREAMS_REQUEST => {
UnknownParameter::try_from(raw).map(Parameter::AddOutgoingStreamsRequest)
}
PARAMETER_TYPE_ADD_INCOMING_STREAMS_REQUEST => {
UnknownParameter::try_from(raw).map(Parameter::AddIncomingStreamsRequest)
}
_ => UnknownParameter::try_from(raw).map(Parameter::Unknown),
}
}
}
impl AsSerializableTlv for Parameter {
fn as_serializable(&self) -> &dyn SerializableTlv {
match self {
Parameter::HeartbeatInfo(p) => p,
Parameter::StateCookie(p) => p,
Parameter::OutgoingSsnResetRequest(p) => p,
Parameter::IncomingSsnResetRequest(p) => p,
Parameter::SsnTsnResetRequest(p)
| Parameter::AddOutgoingStreamsRequest(p)
| Parameter::AddIncomingStreamsRequest(p)
| Parameter::Unknown(p) => p,
Parameter::ReconfigurationResponse(p) => p,
Parameter::SupportedExtensions(p) => p,
Parameter::ForwardTsnSupported(p) => p,
Parameter::ZeroChecksumAcceptable(p) => p,
}
}
}
pub fn parameters_from_bytes(data: &[u8]) -> Result<Vec<Parameter>, ChunkParseError> {
let mut result = Vec::<Parameter>::with_capacity(2);
let mut remaining = data;
while !remaining.is_empty() {
let (raw, next_remaining) = RawParameter::from_bytes(remaining)?;
let error_cause = Parameter::try_from(raw)?;
result.push(error_cause);
remaining = next_remaining;
}
Ok(result)
}
pub fn parameters_serialized_size<T: AsSerializableTlv>(params: &[T]) -> usize {
let mut size: usize = 0;
for (idx, p) in params.iter().enumerate() {
size += p.as_serializable().serialized_size();
if idx != params.len() - 1 {
size = round_up_to_4!(size);
}
}
size
}
pub fn parameters_serialize_to<T: AsSerializableTlv>(params: &[T], out: &mut [u8]) {
let mut offset: usize = 0;
for p in params {
let serializable = p.as_serializable();
let size = serializable.serialized_size();
serializable.serialize_to(&mut out[offset..offset + size]);
offset += round_up_to_4!(size);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fuzz() {
const DATA: &[u8] = &[0, 16, 0, 8, 0, 0, 0, 0];
let _ = parameters_from_bytes(DATA);
}
}