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