use crate::attributes::{stunt_attribute, DecodeAttributeValue, EncodeAttributeValue};
use crate::common::check_buffer_boundaries;
use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
use crate::error::{StunError, StunErrorType};
use crate::Decode;
use crate::Encode;
use bounded_integer::{BoundedU16, BoundedU8};
use std::convert::TryInto;
const ICMP: u16 = 0x8004;
const ICMP_SIZE: usize = 8;
const ICMP_ERROR_DATA_SIZE: usize = 4;
pub type IcmpType = BoundedU8<0, 127>;
pub type IcmpCode = BoundedU16<0, 511>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Icmp {
icmp_type: IcmpType,
icmp_code: IcmpCode,
error_data: [u8; ICMP_ERROR_DATA_SIZE],
}
impl Icmp {
pub fn new(
icmp_type: IcmpType,
icmp_code: IcmpCode,
error_data: [u8; ICMP_ERROR_DATA_SIZE],
) -> Self {
Self {
icmp_type,
icmp_code,
error_data,
}
}
pub fn icmp_type(&self) -> IcmpType {
self.icmp_type
}
pub fn icmp_code(&self) -> IcmpCode {
self.icmp_code
}
pub fn error_data(&self) -> &[u8] {
&self.error_data
}
}
impl DecodeAttributeValue for Icmp {
fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), StunError> {
let raw_value = ctx.raw_value();
check_buffer_boundaries(raw_value, ICMP_SIZE)?;
let (icmp, _) = u16::decode(&raw_value[2..=3])?;
let icmp_type = IcmpType::new((icmp >> 9).try_into()?).ok_or_else(|| {
StunError::new(
StunErrorType::InvalidParam,
format!("Decoded invalid ICMP type {}", icmp >> 9),
)
})?;
let icmp_code = IcmpCode::new(0x01ff & icmp).ok_or_else(|| {
StunError::new(
StunErrorType::InvalidParam,
format!("Decoded invalid ICMP code {}", 0x01ff & icmp),
)
})?;
let mut error_data: [u8; ICMP_ERROR_DATA_SIZE] = [0x0; ICMP_ERROR_DATA_SIZE];
error_data.copy_from_slice(&raw_value[4..ICMP_SIZE]);
Ok((Icmp::new(icmp_type, icmp_code, error_data), ICMP_SIZE))
}
}
impl EncodeAttributeValue for Icmp {
fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, StunError> {
let raw_value = ctx.raw_value_mut();
check_buffer_boundaries(raw_value, ICMP_SIZE)?;
raw_value[..=1].fill(0);
let icmp_type: u16 = self.icmp_type.into();
let icmp_code: u16 = self.icmp_code.into();
let icmp: u16 = (icmp_type << 9) | icmp_code;
icmp.encode(&mut raw_value[2..=3])?;
raw_value[4..ICMP_SIZE].copy_from_slice(&self.error_data);
Ok(ICMP_SIZE)
}
}
impl crate::attributes::AsVerifiable for Icmp {}
stunt_attribute!(Icmp, ICMP);
#[cfg(test)]
mod tests {
use super::*;
use crate::error::StunErrorType;
use crate::StunAttribute;
#[test]
fn decode_icmp_value() {
let dummy_msg = [];
let buffer: [u8; 0] = [];
let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
let result = Icmp::decode(ctx);
assert_eq!(
result.expect_err("Error expected"),
StunErrorType::SmallBuffer
);
let buffer = [0x00, 0x00, 0x03, 0x01, 0x01, 0x02, 0x03];
let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
let result = Icmp::decode(ctx);
assert_eq!(
result.expect_err("Error expected"),
StunErrorType::SmallBuffer
);
let buffer = [0x00, 0x00, 0x03, 0x01, 0x01, 0x02, 0x03, 0x04];
let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
let (attr, size) = Icmp::decode(ctx).expect("Can not decode ICMP attribute");
assert_eq!(size, ICMP_SIZE);
assert_eq!(attr.icmp_type(), IcmpType::new(0x01u8).unwrap());
assert_eq!(attr.icmp_code(), IcmpCode::new(0x101).unwrap());
assert_eq!(attr.error_data(), &buffer[4..]);
}
#[test]
fn encode_icmp_value() {
let dummy_msg: [u8; 0] = [];
let icmp_type = IcmpType::new(127).unwrap();
let icmp_code = IcmpCode::new(511).unwrap();
let error_data = [0x01, 0x02, 0x03, 0x04];
let attr = Icmp::new(icmp_type, icmp_code, error_data);
let mut buffer = [];
let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
let result = attr.encode(ctx);
assert_eq!(
result.expect_err("Error expected"),
StunErrorType::SmallBuffer
);
let mut buffer: [u8; 7] = [0xff; 7];
let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
let result = attr.encode(ctx);
assert_eq!(
result.expect_err("Error expected"),
StunErrorType::SmallBuffer
);
let mut buffer: [u8; 8] = [0xff; 8];
let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
let result = attr.encode(ctx);
assert_eq!(result, Ok(8));
let expected_buffer = [0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04];
assert_eq!(&buffer[..], &expected_buffer[..]);
}
#[test]
fn icmp_stunt_attribute() {
let icmp_type = IcmpType::new(127).expect("Can not create ICMP type");
let icmp_code = IcmpCode::new(511).expect("Can not create ICMP type");
let icmp = Icmp::new(icmp_type, icmp_code, [0x01, 0x02, 0x03, 0x04]);
let attr = StunAttribute::Icmp(icmp);
assert!(attr.is_icmp());
assert!(attr.as_icmp().is_ok());
assert!(attr.as_unknown().is_err());
assert!(!attr.attribute_type().is_comprehension_required());
assert!(attr.attribute_type().is_comprehension_optional());
let dbg_fmt = format!("{:?}", attr);
assert_eq!("Icmp(Icmp { icmp_type: Bounded(127), icmp_code: Bounded(511), error_data: [1, 2, 3, 4] })", dbg_fmt);
}
}