turn_types/attribute/
icmp.rs1use byteorder::{BigEndian, ByteOrder};
12use stun_types::{attribute::*, message::StunParseError};
13
14#[derive(Debug, Clone)]
20pub struct Icmp {
21 typ: u8,
22 code: u8,
23 data: u32,
24}
25
26impl AttributeStaticType for Icmp {
27 const TYPE: AttributeType = AttributeType::new(0x8004);
28}
29
30impl Attribute for Icmp {
31 fn get_type(&self) -> AttributeType {
32 Self::TYPE
33 }
34
35 fn length(&self) -> u16 {
36 8
37 }
38}
39
40impl AttributeWrite for Icmp {
41 fn to_raw(&self) -> RawAttribute<'_> {
42 let mut data = [0; 8];
43 data[2] = self.typ;
44 data[3] = self.code;
45 BigEndian::write_u32(&mut data[4..8], self.data);
46 RawAttribute::new(Self::TYPE, &data).into_owned()
47 }
48 fn write_into_unchecked(&self, dest: &mut [u8]) {
49 self.write_header_unchecked(dest);
50 dest[6] = self.typ;
51 dest[7] = self.code;
52 BigEndian::write_u32(&mut dest[8..12], self.data);
53 }
54}
55
56impl AttributeFromRaw<'_> for Icmp {
57 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
58 where
59 Self: Sized,
60 {
61 Self::try_from(raw)
62 }
63}
64
65impl TryFrom<&RawAttribute<'_>> for Icmp {
66 type Error = StunParseError;
67 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
68 raw.check_type_and_len(Self::TYPE, 8..=8)?;
69 let typ = raw.value[2];
70 let code = raw.value[3];
71 let data = BigEndian::read_u32(&raw.value[4..8]);
72 Ok(Self { typ, code, data })
73 }
74}
75
76impl Icmp {
77 pub fn new(typ: u8, code: u8, data: u32) -> Self {
89 Self { typ, code, data }
90 }
91
92 pub fn icmp_type(&self) -> u8 {
94 self.typ
95 }
96
97 pub fn code(&self) -> u8 {
99 self.code
100 }
101
102 pub fn data(&self) -> u32 {
104 self.data
105 }
106}
107
108impl core::fmt::Display for Icmp {
109 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110 write!(
111 f,
112 "{}: type:{}, code:{}, data:{}",
113 self.get_type(),
114 self.typ,
115 self.code,
116 self.data
117 )
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use alloc::{vec, vec::Vec};
125 use tracing::trace;
126
127 #[test]
128 fn icmp() {
129 let _log = crate::tests::test_init_log();
130 let mapped = Icmp::new(2, 4, 8);
131 assert_eq!(mapped.get_type(), Icmp::TYPE);
132 assert_eq!(mapped.icmp_type(), 2);
133 assert_eq!(mapped.code(), 4);
134 assert_eq!(mapped.data(), 8);
135 }
136
137 #[test]
138 fn icmp_raw() {
139 let _log = crate::tests::test_init_log();
140 let mapped = Icmp::new(2, 4, 8);
141 let raw: RawAttribute = mapped.to_raw();
142 trace!("{}", raw);
143 assert_eq!(raw.get_type(), Icmp::TYPE);
144 let mapped2 = Icmp::try_from(&raw).unwrap();
145 assert_eq!(mapped2.get_type(), Icmp::TYPE);
146 assert_eq!(mapped2.icmp_type(), 2);
147 assert_eq!(mapped2.code(), 4);
148 assert_eq!(mapped2.data(), 8);
149 }
150
151 #[test]
152 fn icmp_raw_short() {
153 let _log = crate::tests::test_init_log();
154 let mapped = Icmp::new(2, 4, 8);
155 let raw: RawAttribute = mapped.to_raw();
156 assert_eq!(raw.get_type(), Icmp::TYPE);
157 let mut data: Vec<_> = raw.clone().into();
159 let len = data.len();
160 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
161 assert!(matches!(
162 Icmp::try_from(&RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()),
163 Err(StunParseError::Truncated {
164 expected: _,
165 actual: _,
166 })
167 ));
168 }
169
170 #[test]
171 fn icmp_raw_wrong_type() {
172 let _log = crate::tests::test_init_log();
173 let mapped = Icmp::new(2, 4, 8);
174 let raw: RawAttribute = mapped.to_raw();
175 assert_eq!(raw.get_type(), Icmp::TYPE);
176 let mut data: Vec<_> = raw.clone().into();
178 BigEndian::write_u16(&mut data[0..2], 0);
179 assert!(matches!(
180 Icmp::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
181 Err(StunParseError::WrongAttributeImplementation)
182 ));
183 }
184
185 #[test]
186 fn icmp_write_into() {
187 let _log = crate::tests::test_init_log();
188 let mapped = Icmp::new(2, 4, 8);
189 let raw: RawAttribute = mapped.to_raw();
190 let mut dest = vec![0; raw.padded_len()];
191 mapped.write_into(&mut dest).unwrap();
192 let raw = RawAttribute::from_bytes(&dest).unwrap();
193 let mapped2 = Icmp::try_from(&raw).unwrap();
194 assert_eq!(mapped2.get_type(), Icmp::TYPE);
195 assert_eq!(mapped2.icmp_type(), 2);
196 assert_eq!(mapped2.code(), 4);
197 assert_eq!(mapped2.data(), 8);
198 }
199
200 #[test]
201 #[should_panic = "out of range"]
202 fn icmp_write_into_unchecked() {
203 let _log = crate::tests::test_init_log();
204 let mapped = Icmp::new(2, 4, 8);
205 let raw: RawAttribute = mapped.to_raw();
206 let mut dest = vec![0; raw.padded_len() - 1];
207 mapped.write_into_unchecked(&mut dest);
208 }
209}