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
14pub type IcmpType = BoundedU8<0, 127>;
33
34pub type IcmpCode = BoundedU16<0, 511>;
53
54#[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 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 pub fn icmp_type(&self) -> IcmpType {
96 self.icmp_type
97 }
98
99 pub fn icmp_code(&self) -> IcmpCode {
101 self.icmp_code
102 }
103
104 pub fn error_data(&self) -> &[u8] {
114 &self.error_data
115 }
116}
117
118impl 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}