stun_types/attribute/
alternate.rs1use alloc::borrow::ToOwned;
12use alloc::string::{String, ToString};
13use core::convert::TryFrom;
14use core::net::SocketAddr;
15
16use crate::message::StunParseError;
17
18use super::{
19 Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
20 AttributeWriteExt, MappedSocketAddr, RawAttribute,
21};
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct AlternateServer {
26 addr: MappedSocketAddr,
27}
28
29impl AttributeStaticType for AlternateServer {
30 const TYPE: AttributeType = AttributeType(0x8023);
31}
32impl Attribute for AlternateServer {
33 fn get_type(&self) -> AttributeType {
34 Self::TYPE
35 }
36
37 fn length(&self) -> u16 {
38 self.addr.length()
39 }
40}
41
42impl AttributeWrite for AlternateServer {
43 fn to_raw(&self) -> RawAttribute<'_> {
44 self.addr.to_raw(AlternateServer::TYPE)
45 }
46 fn write_into_unchecked(&self, dest: &mut [u8]) {
47 self.write_header_unchecked(dest);
48 self.addr.write_into_unchecked(&mut dest[4..]);
49 }
50}
51
52impl AttributeFromRaw<'_> for AlternateServer {
53 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError> {
54 Self::try_from(raw)
55 }
56}
57
58impl TryFrom<&RawAttribute<'_>> for AlternateServer {
59 type Error = StunParseError;
60
61 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
62 raw.check_type_and_len(Self::TYPE, ..)?;
63 let addr = MappedSocketAddr::from_raw(raw)?;
64 Ok(Self { addr })
65 }
66}
67
68impl AlternateServer {
69 pub fn new(addr: SocketAddr) -> Self {
80 Self {
81 addr: MappedSocketAddr::new(addr),
82 }
83 }
84
85 pub fn server(&self) -> SocketAddr {
96 self.addr.addr()
97 }
98}
99
100impl core::fmt::Display for AlternateServer {
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 write!(f, "{}: {}", AlternateServer::TYPE, self.addr)
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct AlternateDomain {
109 domain: String,
110}
111
112impl AttributeStaticType for AlternateDomain {
113 const TYPE: AttributeType = AttributeType(0x8003);
114}
115
116impl Attribute for AlternateDomain {
117 fn get_type(&self) -> AttributeType {
118 Self::TYPE
119 }
120 fn length(&self) -> u16 {
121 self.domain.len() as u16
122 }
123}
124impl AttributeFromRaw<'_> for AlternateDomain {
125 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
126 where
127 Self: Sized,
128 {
129 Self::try_from(raw)
130 }
131}
132impl TryFrom<&RawAttribute<'_>> for AlternateDomain {
133 type Error = StunParseError;
134
135 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
136 raw.check_type_and_len(Self::TYPE, ..)?;
137 Ok(Self {
139 domain: core::str::from_utf8(&raw.value)
140 .map_err(|_| StunParseError::InvalidAttributeData)?
141 .to_owned(),
142 })
143 }
144}
145impl AttributeWrite for AlternateDomain {
146 fn to_raw(&self) -> RawAttribute<'_> {
147 RawAttribute::new(AlternateDomain::TYPE, self.domain.as_bytes())
148 }
149 fn write_into_unchecked(&self, dest: &mut [u8]) {
150 let len = self.padded_len();
151 self.write_header_unchecked(dest);
152 dest[4..4 + self.domain.len()].copy_from_slice(self.domain.as_bytes());
153 let offset = 4 + self.domain.len();
154 if len > offset {
155 dest[offset..len].fill(0);
156 }
157 }
158}
159
160impl AlternateDomain {
161 pub fn new(domain: &str) -> Self {
172 Self {
173 domain: domain.to_string(),
174 }
175 }
176
177 pub fn domain(&self) -> &str {
188 &self.domain
189 }
190}
191
192impl core::fmt::Display for AlternateDomain {
193 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
194 write!(f, "{}: {}", AlternateDomain::TYPE, self.domain)
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
201
202 use super::*;
203 use alloc::vec;
204 use alloc::vec::Vec;
205 use byteorder::{BigEndian, ByteOrder};
206 use tracing::trace;
207
208 const ADDRS: [SocketAddr; 2] = [
209 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)), 40000),
210 SocketAddr::new(
211 IpAddr::V6(Ipv6Addr::new(
212 0xfd2, 0x3456, 0x789a, 0x01, 0x0, 0x0, 0x0, 0x1,
213 )),
214 41000,
215 ),
216 ];
217
218 #[test]
219 fn alternate_server() {
220 let _log = crate::tests::test_init_log();
221 for addr in ADDRS {
222 let mapped = AlternateServer::new(addr);
223 trace!("{mapped}");
224 assert_eq!(mapped.server(), addr);
225 match addr {
226 SocketAddr::V4(_) => assert_eq!(mapped.length(), 8),
227 SocketAddr::V6(_) => assert_eq!(mapped.length(), 20),
228 }
229 }
230 }
231
232 #[test]
233 fn alternate_server_raw() {
234 let _log = crate::tests::test_init_log();
235 for addr in ADDRS {
236 let mapped = AlternateServer::new(addr);
237 let raw = RawAttribute::from(&mapped);
238 match addr {
239 SocketAddr::V4(_) => assert_eq!(raw.length(), 8),
240 SocketAddr::V6(_) => assert_eq!(raw.length(), 20),
241 }
242 trace!("{raw}");
243 assert_eq!(raw.get_type(), AlternateServer::TYPE);
244 let mapped2 = AlternateServer::try_from(&raw).unwrap();
245 assert_eq!(mapped2.server(), addr);
246 }
247 }
248
249 #[test]
250 fn alternate_server_raw_short() {
251 let _log = crate::tests::test_init_log();
252 for addr in ADDRS {
253 let mapped = AlternateServer::new(addr);
254 let raw = RawAttribute::from(&mapped);
255 let mut data: Vec<_> = raw.clone().into();
257 let len = data.len();
258 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
259 assert!(matches!(
260 AlternateServer::try_from(
261 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
262 ),
263 Err(StunParseError::Truncated {
264 expected: _,
265 actual: _
266 })
267 ));
268 }
269 }
270
271 #[test]
272 fn alternate_server_raw_wrong_type() {
273 let _log = crate::tests::test_init_log();
274 for addr in ADDRS {
275 let mapped = AlternateServer::new(addr);
276 let raw = RawAttribute::from(&mapped);
277 let mut data: Vec<_> = raw.clone().into();
279 BigEndian::write_u16(&mut data[0..2], 0);
280 assert!(matches!(
281 AlternateServer::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
282 Err(StunParseError::WrongAttributeImplementation)
283 ));
284 }
285 }
286
287 #[test]
288 fn alternate_server_write_into() {
289 let _log = crate::tests::test_init_log();
290 for addr in ADDRS {
291 let mapped = AlternateServer::new(addr);
292 let raw = RawAttribute::from(&mapped);
293
294 let mut dest = vec![0; raw.padded_len()];
295 mapped.write_into(&mut dest).unwrap();
296 let raw = RawAttribute::from_bytes(&dest).unwrap();
297 let mapped2 = AlternateServer::try_from(&raw).unwrap();
298 assert_eq!(mapped2.server(), addr);
299 }
300 }
301
302 #[test]
303 #[should_panic(expected = "out of range")]
304 fn alternate_server_write_into_unchecked() {
305 let _log = crate::tests::test_init_log();
306 let mapped = AlternateServer::new(ADDRS[0]);
307 let raw = RawAttribute::from(&mapped);
308
309 let mut dest = vec![0; raw.padded_len() - 1];
310 mapped.write_into_unchecked(&mut dest);
311 }
312
313 const DOMAIN: &str = "example.com";
314
315 #[test]
316 fn alternative_domain() {
317 let _log = crate::tests::test_init_log();
318 let attr = AlternateDomain::new(DOMAIN);
319 trace!("{attr}");
320 assert_eq!(attr.domain(), DOMAIN);
321 assert_eq!(attr.length() as usize, DOMAIN.len());
322 }
323
324 #[test]
325 fn alternative_domain_raw() {
326 let _log = crate::tests::test_init_log();
327 let attr = AlternateDomain::new(DOMAIN);
328 let raw = RawAttribute::from(&attr);
329 trace!("{raw}");
330 assert_eq!(raw.get_type(), AlternateDomain::TYPE);
331 let mapped2 = AlternateDomain::try_from(&raw).unwrap();
332 assert_eq!(mapped2.domain(), DOMAIN);
333 }
334
335 #[test]
336 fn alternative_domain_raw_wrong_type() {
337 let _log = crate::tests::test_init_log();
338 let attr = AlternateDomain::new(DOMAIN);
339 let raw = RawAttribute::from(&attr);
340 let mut data: Vec<_> = raw.clone().into();
341 BigEndian::write_u16(&mut data[0..2], 0);
343 assert!(matches!(
344 AlternateDomain::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
345 Err(StunParseError::WrongAttributeImplementation)
346 ));
347 }
348
349 #[test]
350 fn alternative_domain_raw_invalid_utf8() {
351 let _log = crate::tests::test_init_log();
352 let attr = AlternateDomain::new(DOMAIN);
353 let raw = RawAttribute::from(&attr);
354
355 let mut data: Vec<_> = raw.clone().into();
357 data[8] = 0x88;
358 assert!(matches!(
359 AlternateDomain::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
360 Err(StunParseError::InvalidAttributeData)
361 ));
362 }
363
364 #[test]
365 fn alternative_domain_write_into() {
366 let _log = crate::tests::test_init_log();
367 let attr = AlternateDomain::new(DOMAIN);
368 let raw = RawAttribute::from(&attr);
369
370 let mut dest = vec![0; raw.padded_len()];
371 attr.write_into(&mut dest).unwrap();
372 let raw = RawAttribute::from_bytes(&dest).unwrap();
373 let mapped2 = AlternateDomain::try_from(&raw).unwrap();
374 assert_eq!(mapped2.domain(), DOMAIN);
375 }
376
377 #[test]
378 #[should_panic(expected = "out of range")]
379 fn alternative_domain_write_into_unchecked() {
380 let _log = crate::tests::test_init_log();
381 let attr = AlternateDomain::new(DOMAIN);
382 let raw = RawAttribute::from(&attr);
383
384 let mut dest = vec![0; raw.padded_len() - 1];
385 attr.write_into_unchecked(&mut dest);
386 }
387}