stun_rs/attributes/turn/
icmp.rs

1use crate::attributes::{stunt_attribute, DecodeAttributeValue, EncodeAttributeValue};
2use crate::common::check_buffer_boundaries;
3use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
4use crate::error::{StunError, StunErrorType};
5use crate::Decode;
6use crate::Encode;
7use bounded_integer::{BoundedU16, BoundedU8};
8use std::convert::TryInto;
9
10const ICMP: u16 = 0x8004;
11const ICMP_SIZE: usize = 8;
12const ICMP_ERROR_DATA_SIZE: usize = 4;
13
14/// This type represents the value of the `ICMP` type.  Its interpretation
15/// depends on whether the `ICMP` was received over IPv4 or IPv6.
16/// Valid values are in the range from 0 to 127.
17/// # Examples
18/// ```rust
19/// # use stun_rs::attributes::turn::IcmpType;
20/// // Use maximum value
21/// let icmp_type = IcmpType::new(127);
22/// assert!(icmp_type.is_some());
23///
24/// // Use minimum value
25/// let icmp_type = IcmpType::new(0);
26/// assert!(icmp_type.is_some());
27///
28/// // Use out of range value
29/// let icmp_type = IcmpType::new(128);
30/// assert!(icmp_type.is_none());
31///```
32pub type IcmpType = BoundedU8<0, 127>;
33
34/// This type represents the value of the `ICMP` code.  Its interpretation
35/// depends on whether the `ICMP` was received over IPv4 or IPv6.
36/// Valid values are in the range from 0 to 511.
37/// # Examples
38///```rust
39/// # use stun_rs::attributes::turn::IcmpCode;
40/// // Use maximum value
41/// let icmp_code = IcmpCode::new(511);
42/// assert!(icmp_code.is_some());
43///
44/// // Use minimum value
45/// let icmp_code = IcmpCode::new(0);
46/// assert!(icmp_code.is_some());
47///
48/// // Use out of range value
49/// let icmp_code = IcmpCode::new(512);
50/// assert!(icmp_code.is_none());
51///```
52pub type IcmpCode = BoundedU16<0, 511>;
53
54/// This attribute is used by servers to signal the reason a `UDP` packet
55/// was dropped.
56/// # Examples
57/// ```rust
58/// # use stun_rs::attributes::turn::{Icmp, IcmpCode, IcmpType};
59/// # use std::error::Error;
60/// #
61/// # fn main() -> Result<(), Box<dyn Error>> {
62/// let icmp_type = IcmpType::new(127).ok_or_else(|| "Invalid ICMP type")?;
63/// let icmp_code = IcmpCode::new(511).ok_or_else(|| "Invalid ICMP code")?;
64/// let attr = Icmp::new(icmp_type, icmp_code, [0x01, 0x02, 0x03, 0x04]);
65///
66/// assert_eq!(attr.icmp_type(), icmp_type);
67/// assert_eq!(attr.icmp_code(), icmp_code);
68/// assert_eq!(attr.error_data(), &[0x01, 0x02, 0x03, 0x04]);
69/// #
70/// # Ok(())
71/// # }
72///```
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct Icmp {
75    icmp_type: IcmpType,
76    icmp_code: IcmpCode,
77    error_data: [u8; ICMP_ERROR_DATA_SIZE],
78}
79
80impl Icmp {
81    /// Creates a new [`Icmp`] attribute
82    pub fn new(
83        icmp_type: IcmpType,
84        icmp_code: IcmpCode,
85        error_data: [u8; ICMP_ERROR_DATA_SIZE],
86    ) -> Self {
87        Self {
88            icmp_type,
89            icmp_code,
90            error_data,
91        }
92    }
93
94    /// Returns the `ICMP` type.
95    pub fn icmp_type(&self) -> IcmpType {
96        self.icmp_type
97    }
98
99    /// Returns the error data.
100    pub fn icmp_code(&self) -> IcmpCode {
101        self.icmp_code
102    }
103
104    /// Returns the error data. This field size is 4 bytes long.
105    /// If the `ICMPv6` type is 2 ("Packet too big" message) or `ICMPv4`
106    /// type is 3 (Destination Unreachable) and Code is 4 (fragmentation
107    /// needed and `DF` set), the Error Data field will be set to the Maximum
108    /// Transmission Unit of the next-hop link (Section 3.2 of
109    /// [`RFC4443`](https://datatracker.ietf.org/doc/html/rfc4443#section-3.2)
110    /// and Section 4 of [`RFC1191`](https://datatracker.ietf.org/doc/html/rfc1191#section-4)).
111    /// For other `ICMPv6` types and `ICMPv4` types and codes, the Error Data field
112    /// MUST be set to zero.
113    pub fn error_data(&self) -> &[u8] {
114        &self.error_data
115    }
116}
117
118// Format of ICMP Attribute:
119//  0                   1                   2                   3
120//  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
121// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
122// |  Reserved                     |  ICMP Type  |  ICMP Code      |
123// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
124// |                          Error Data                           |
125// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
126
127impl DecodeAttributeValue for Icmp {
128    fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), StunError> {
129        let raw_value = ctx.raw_value();
130        check_buffer_boundaries(raw_value, ICMP_SIZE)?;
131        let (icmp, _) = u16::decode(&raw_value[2..=3])?;
132        let icmp_type = IcmpType::new((icmp >> 9).try_into()?).ok_or_else(|| {
133            StunError::new(
134                StunErrorType::InvalidParam,
135                format!("Decoded invalid ICMP type {}", icmp >> 9),
136            )
137        })?;
138        let icmp_code = IcmpCode::new(0x01ff & icmp).ok_or_else(|| {
139            StunError::new(
140                StunErrorType::InvalidParam,
141                format!("Decoded invalid ICMP code {}", 0x01ff & icmp),
142            )
143        })?;
144        let mut error_data: [u8; ICMP_ERROR_DATA_SIZE] = [0x0; ICMP_ERROR_DATA_SIZE];
145        error_data.copy_from_slice(&raw_value[4..ICMP_SIZE]);
146        Ok((Icmp::new(icmp_type, icmp_code, error_data), ICMP_SIZE))
147    }
148}
149
150impl EncodeAttributeValue for Icmp {
151    fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, StunError> {
152        let raw_value = ctx.raw_value_mut();
153        check_buffer_boundaries(raw_value, ICMP_SIZE)?;
154        raw_value[..=1].fill(0);
155        let icmp_type: u16 = self.icmp_type.into();
156        let icmp_code: u16 = self.icmp_code.into();
157        let icmp: u16 = (icmp_type << 9) | icmp_code;
158        icmp.encode(&mut raw_value[2..=3])?;
159        raw_value[4..ICMP_SIZE].copy_from_slice(&self.error_data);
160        Ok(ICMP_SIZE)
161    }
162}
163
164impl crate::attributes::AsVerifiable for Icmp {}
165
166stunt_attribute!(Icmp, ICMP);
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use crate::error::StunErrorType;
172    use crate::StunAttribute;
173
174    #[test]
175    fn decode_icmp_value() {
176        let dummy_msg = [];
177        let buffer: [u8; 0] = [];
178        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
179        let result = Icmp::decode(ctx);
180        assert_eq!(
181            result.expect_err("Error expected"),
182            StunErrorType::SmallBuffer
183        );
184
185        let buffer = [0x00, 0x00, 0x03, 0x01, 0x01, 0x02, 0x03];
186        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
187        let result = Icmp::decode(ctx);
188        assert_eq!(
189            result.expect_err("Error expected"),
190            StunErrorType::SmallBuffer
191        );
192
193        let buffer = [0x00, 0x00, 0x03, 0x01, 0x01, 0x02, 0x03, 0x04];
194        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
195        let (attr, size) = Icmp::decode(ctx).expect("Can not decode ICMP attribute");
196        assert_eq!(size, ICMP_SIZE);
197        assert_eq!(attr.icmp_type(), IcmpType::new(0x01u8).unwrap());
198        assert_eq!(attr.icmp_code(), IcmpCode::new(0x101).unwrap());
199        assert_eq!(attr.error_data(), &buffer[4..]);
200    }
201
202    #[test]
203    fn encode_icmp_value() {
204        let dummy_msg: [u8; 0] = [];
205        let icmp_type = IcmpType::new(127).unwrap();
206        let icmp_code = IcmpCode::new(511).unwrap();
207        let error_data = [0x01, 0x02, 0x03, 0x04];
208        let attr = Icmp::new(icmp_type, icmp_code, error_data);
209
210        let mut buffer = [];
211        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
212        let result = attr.encode(ctx);
213        assert_eq!(
214            result.expect_err("Error expected"),
215            StunErrorType::SmallBuffer
216        );
217
218        let mut buffer: [u8; 7] = [0xff; 7];
219        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
220        let result = attr.encode(ctx);
221        assert_eq!(
222            result.expect_err("Error expected"),
223            StunErrorType::SmallBuffer
224        );
225
226        let mut buffer: [u8; 8] = [0xff; 8];
227        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
228        let result = attr.encode(ctx);
229        assert_eq!(result, Ok(8));
230
231        let expected_buffer = [0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04];
232        assert_eq!(&buffer[..], &expected_buffer[..]);
233    }
234
235    #[test]
236    fn icmp_stunt_attribute() {
237        let icmp_type = IcmpType::new(127).expect("Can not create ICMP type");
238        let icmp_code = IcmpCode::new(511).expect("Can not create ICMP type");
239        let icmp = Icmp::new(icmp_type, icmp_code, [0x01, 0x02, 0x03, 0x04]);
240
241        let attr = StunAttribute::Icmp(icmp);
242        assert!(attr.is_icmp());
243        assert!(attr.as_icmp().is_ok());
244        assert!(attr.as_unknown().is_err());
245
246        assert!(!attr.attribute_type().is_comprehension_required());
247        assert!(attr.attribute_type().is_comprehension_optional());
248
249        let dbg_fmt = format!("{:?}", attr);
250        assert_eq!("Icmp(Icmp { icmp_type: Bounded(127), icmp_code: Bounded(511), error_data: [1, 2, 3, 4] })", dbg_fmt);
251    }
252}