stun_types/attribute/
error.rs

1// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use alloc::borrow::ToOwned;
10use alloc::string::String;
11use alloc::vec;
12use alloc::vec::Vec;
13use core::convert::TryFrom;
14
15use byteorder::{BigEndian, ByteOrder};
16
17use crate::message::{StunParseError, StunWriteError};
18
19use super::{
20    Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
21    AttributeWriteExt, RawAttribute,
22};
23
24/// The ErrorCode [`Attribute`]
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct ErrorCode {
27    code: u16,
28    reason: String,
29}
30impl AttributeStaticType for ErrorCode {
31    const TYPE: AttributeType = AttributeType(0x0009);
32}
33impl Attribute for ErrorCode {
34    fn get_type(&self) -> AttributeType {
35        Self::TYPE
36    }
37
38    fn length(&self) -> u16 {
39        self.reason.len() as u16 + 4
40    }
41}
42
43impl AttributeWrite for ErrorCode {
44    fn to_raw(&self) -> RawAttribute<'_> {
45        let mut data = Vec::with_capacity(self.length() as usize);
46        data.push(0u8);
47        data.push(0u8);
48        data.push((self.code / 100) as u8);
49        data.push((self.code % 100) as u8);
50        data.extend(self.reason.as_bytes());
51        RawAttribute::new_owned(ErrorCode::TYPE, data.into_boxed_slice())
52    }
53
54    fn write_into_unchecked(&self, dest: &mut [u8]) {
55        let len = self.padded_len();
56        let mut offset = self.write_header_unchecked(dest);
57        offset += self.write_into_data(&mut dest[offset..]);
58        if len - offset > 0 {
59            dest[offset..len].fill(0);
60        }
61    }
62}
63
64impl AttributeFromRaw<'_> for ErrorCode {
65    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
66    where
67        Self: Sized,
68    {
69        Self::try_from(raw)
70    }
71}
72
73impl TryFrom<&RawAttribute<'_>> for ErrorCode {
74    type Error = StunParseError;
75
76    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
77        raw.check_type_and_len(Self::TYPE, 4..=763 + 4)?;
78        let code_h = (raw.value[2] & 0x7) as u16;
79        let code_tens = raw.value[3] as u16;
80        if !(3..7).contains(&code_h) || code_tens > 99 {
81            return Err(StunParseError::InvalidAttributeData);
82        }
83        let code = code_h * 100 + code_tens;
84        Ok(Self {
85            code,
86            reason: core::str::from_utf8(&raw.value[4..])
87                .map_err(|_| StunParseError::InvalidAttributeData)?
88                .to_owned(),
89        })
90    }
91}
92
93/// Builder for an [`ErrorCode`]
94#[derive(Debug)]
95pub struct ErrorCodeBuilder<'reason> {
96    code: u16,
97    reason: Option<&'reason str>,
98}
99
100impl<'reason> ErrorCodeBuilder<'reason> {
101    fn new(code: u16) -> Self {
102        Self { code, reason: None }
103    }
104
105    /// Set the custom reason for this [`ErrorCode`]
106    pub fn reason(mut self, reason: &'reason str) -> Self {
107        self.reason = Some(reason);
108        self
109    }
110
111    /// Create the [`ErrorCode`] with the configured paramaters
112    ///
113    /// # Errors
114    ///
115    /// - When the code value is out of range [300, 699]
116    pub fn build(self) -> Result<ErrorCode, StunWriteError> {
117        if !(300..700).contains(&self.code) {
118            return Err(StunWriteError::OutOfRange {
119                value: self.code as usize,
120                min: 300,
121                max: 699,
122            });
123        }
124        let reason = self
125            .reason
126            .unwrap_or_else(|| ErrorCode::default_reason_for_code(self.code))
127            .to_owned();
128        Ok(ErrorCode {
129            code: self.code,
130            reason,
131        })
132    }
133}
134
135impl ErrorCode {
136    /// Try an alternate server.  The
137    /// [`AlternateServer`](crate::attribute::alternate::AlternateServer) or
138    /// [`AlternateDomain`](crate::attribute::alternate::AlternateDomain) contains the location of
139    /// where to forward this request.
140    pub const TRY_ALTERNATE: u16 = 300;
141    /// The request was malformed and could not be processed.
142    pub const BAD_REQUEST: u16 = 400;
143    /// The required credentials were not found or did not match.
144    pub const UNAUTHORIZED: u16 = 401;
145    /// Not allowed to access this resource.
146    pub const FORBIDDEN: u16 = 403;
147    /// An unknown comprehension required attribute was present.  The [`UnknownAttributes`]
148    /// contains the specific attribute/s.
149    pub const UNKNOWN_ATTRIBUTE: u16 = 420;
150    /// The allocation already exists on this server.
151    pub const ALLOCATION_MISMATCH: u16 = 437;
152    /// The nonce is no longer valid.
153    pub const STALE_NONCE: u16 = 438;
154    /// The address family (IPv4, IPv6) is not supported.
155    pub const ADDRESS_FAMILY_NOT_SUPPORTED: u16 = 440;
156    /// Incorrect credentials provided.
157    pub const WRONG_CREDENTIALS: u16 = 441;
158    /// The transport protocol (UDP, TCP) is not supported.
159    pub const UNSUPPORTED_TRANSPORT_PROTOCOL: u16 = 442;
160    /// The peer address family does not match the TURN allocation.
161    pub const PEER_ADDRESS_FAMILY_MISMATCH: u16 = 443;
162    /// The connection already exists.
163    pub const CONNECTION_ALREADY_EXISTS: u16 = 446;
164    /// The connection could not be established due to timeout or another failure.
165    pub const CONNECTION_TIMEOUT_OR_FAILURE: u16 = 447;
166    /// This username has reached its limit of allocations currently allowed.
167    pub const ALLOCATION_QUOTA_REACHED: u16 = 486;
168    /// Requestor must switch ICE roles.
169    pub const ROLE_CONFLICT: u16 = 487;
170    /// An unspecificed error has occurred.
171    pub const SERVER_ERROR: u16 = 500;
172    /// The server does not have capacity to handle this request.
173    pub const INSUFFICIENT_CAPACITY: u16 = 508;
174
175    /// Create a builder for creating a new [`ErrorCode`] [`Attribute`]
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// # use stun_types::attribute::*;
181    /// let error = ErrorCode::builder (400).reason("bad error").build().unwrap();
182    /// assert_eq!(error.code(), 400);
183    /// assert_eq!(error.reason(), "bad error");
184    /// ```
185    pub fn builder<'reason>(code: u16) -> ErrorCodeBuilder<'reason> {
186        ErrorCodeBuilder::new(code)
187    }
188
189    /// Create a new [`ErrorCode`] [`Attribute`]
190    ///
191    /// # Errors
192    ///
193    /// - When the code value is out of range [300, 699]
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// # use stun_types::attribute::*;
199    /// let error = ErrorCode::new (400, "bad error").unwrap();
200    /// assert_eq!(error.code(), 400);
201    /// assert_eq!(error.reason(), "bad error");
202    /// ```
203    pub fn new(code: u16, reason: &str) -> Result<Self, StunWriteError> {
204        if !(300..700).contains(&code) {
205            return Err(StunWriteError::OutOfRange {
206                value: code as usize,
207                min: 300,
208                max: 699,
209            });
210        }
211        Ok(Self {
212            code,
213            reason: reason.to_owned(),
214        })
215    }
216
217    /// The error code value
218    ///
219    /// # Examples
220    ///
221    /// ```
222    /// # use stun_types::attribute::*;
223    /// let error = ErrorCode::new (400, "bad error").unwrap();
224    /// assert_eq!(error.code(), 400);
225    /// ```
226    pub fn code(&self) -> u16 {
227        self.code
228    }
229
230    /// The error code reason string
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// # use stun_types::attribute::*;
236    /// let error = ErrorCode::new (400, "bad error").unwrap();
237    /// assert_eq!(error.reason(), "bad error");
238    /// ```
239    pub fn reason(&self) -> &str {
240        &self.reason
241    }
242
243    /// Return some default reason strings for some error code values
244    ///
245    /// Currently the following are supported.
246    ///
247    /// ```
248    /// # use stun_types::attribute::*;
249    /// assert_eq!(ErrorCode::default_reason_for_code(300), "Try Alternate");
250    /// assert_eq!(ErrorCode::default_reason_for_code(400), "Bad Request");
251    /// assert_eq!(ErrorCode::default_reason_for_code(401), "Unauthorized");
252    /// assert_eq!(ErrorCode::default_reason_for_code(403), "Forbidden");
253    /// assert_eq!(ErrorCode::default_reason_for_code(420), "Unknown Attribute");
254    /// assert_eq!(ErrorCode::default_reason_for_code(437), "Allocation Mismatch");
255    /// assert_eq!(ErrorCode::default_reason_for_code(438), "Stale Nonce");
256    /// assert_eq!(ErrorCode::default_reason_for_code(440), "Address Family Not Supported");
257    /// assert_eq!(ErrorCode::default_reason_for_code(441), "Wrong Credentials");
258    /// assert_eq!(ErrorCode::default_reason_for_code(442), "Unsupported Transport Protocol");
259    /// assert_eq!(ErrorCode::default_reason_for_code(443), "Peer Address Family Mismatch");
260    /// assert_eq!(ErrorCode::default_reason_for_code(446), "Connection Already Exists");
261    /// assert_eq!(ErrorCode::default_reason_for_code(447), "Connection Timeout or Failure");
262    /// assert_eq!(ErrorCode::default_reason_for_code(486), "Allocation Quota Reached");
263    /// assert_eq!(ErrorCode::default_reason_for_code(487), "Role Conflict");
264    /// assert_eq!(ErrorCode::default_reason_for_code(500), "Server Error");
265    /// assert_eq!(ErrorCode::default_reason_for_code(508), "Insufficient Capacity");
266    /// ```
267    pub fn default_reason_for_code(code: u16) -> &'static str {
268        match code {
269            Self::TRY_ALTERNATE => "Try Alternate",
270            Self::BAD_REQUEST => "Bad Request",
271            Self::UNAUTHORIZED => "Unauthorized",
272            Self::FORBIDDEN => "Forbidden",
273            Self::UNKNOWN_ATTRIBUTE => "Unknown Attribute",
274            Self::ALLOCATION_MISMATCH => "Allocation Mismatch",
275            Self::STALE_NONCE => "Stale Nonce",
276            Self::ADDRESS_FAMILY_NOT_SUPPORTED => "Address Family Not Supported",
277            Self::WRONG_CREDENTIALS => "Wrong Credentials",
278            Self::UNSUPPORTED_TRANSPORT_PROTOCOL => "Unsupported Transport Protocol",
279            Self::PEER_ADDRESS_FAMILY_MISMATCH => "Peer Address Family Mismatch",
280            Self::CONNECTION_ALREADY_EXISTS => "Connection Already Exists",
281            Self::CONNECTION_TIMEOUT_OR_FAILURE => "Connection Timeout or Failure",
282            Self::ALLOCATION_QUOTA_REACHED => "Allocation Quota Reached",
283            Self::ROLE_CONFLICT => "Role Conflict",
284            Self::SERVER_ERROR => "Server Error",
285            Self::INSUFFICIENT_CAPACITY => "Insufficient Capacity",
286            _ => "Unknown",
287        }
288    }
289
290    fn write_into_data(&self, dest: &mut [u8]) -> usize {
291        dest[0] = 0u8;
292        dest[1] = 0u8;
293        dest[2] = (self.code / 100) as u8;
294        dest[3] = (self.code % 100) as u8;
295        let bytes = self.reason.as_bytes();
296        dest[4..4 + bytes.len()].copy_from_slice(bytes);
297        4 + bytes.len()
298    }
299}
300
301impl core::fmt::Display for ErrorCode {
302    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
303        write!(f, "{}: {} '{}'", Self::TYPE, self.code, self.reason)
304    }
305}
306
307/// The UnknownAttributes [`Attribute`]
308#[derive(Debug, Clone, PartialEq, Eq)]
309pub struct UnknownAttributes {
310    attributes: Vec<AttributeType>,
311}
312impl AttributeStaticType for UnknownAttributes {
313    const TYPE: AttributeType = AttributeType(0x000A);
314}
315impl Attribute for UnknownAttributes {
316    fn get_type(&self) -> AttributeType {
317        Self::TYPE
318    }
319    fn length(&self) -> u16 {
320        (self.attributes.len() as u16) * 2
321    }
322}
323impl AttributeWrite for UnknownAttributes {
324    fn to_raw(&self) -> RawAttribute<'_> {
325        let mut data = vec![0; self.length() as usize];
326        self.write_into_data(&mut data);
327        RawAttribute::new_owned(UnknownAttributes::TYPE, data.into_boxed_slice())
328    }
329
330    fn write_into_unchecked(&self, dest: &mut [u8]) {
331        let len = self.padded_len();
332        let mut offset = self.write_header_unchecked(dest);
333        offset += self.write_into_data(&mut dest[offset..]);
334        if len - offset > 0 {
335            dest[offset..len].fill(0);
336        }
337    }
338}
339
340impl AttributeFromRaw<'_> for UnknownAttributes {
341    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
342    where
343        Self: Sized,
344    {
345        Self::try_from(raw)
346    }
347}
348
349impl TryFrom<&RawAttribute<'_>> for UnknownAttributes {
350    type Error = StunParseError;
351
352    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
353        if raw.header.atype != Self::TYPE {
354            return Err(StunParseError::WrongAttributeImplementation);
355        }
356        if raw.value.len() % 2 != 0 {
357            /* all attributes are 16-bits */
358            return Err(StunParseError::Truncated {
359                expected: raw.value.len() + 1,
360                actual: raw.value.len(),
361            });
362        }
363        let mut attrs = vec![];
364        for attr in raw.value.chunks_exact(2) {
365            attrs.push(BigEndian::read_u16(attr).into());
366        }
367        Ok(Self { attributes: attrs })
368    }
369}
370impl UnknownAttributes {
371    /// Create a new unknown attributes [`Attribute`]
372    ///
373    /// # Examples
374    ///
375    /// ```
376    /// # use stun_types::attribute::*;
377    /// let unknown = UnknownAttributes::new(&[Username::TYPE]);
378    /// assert!(unknown.has_attribute(Username::TYPE));
379    /// ```
380    pub fn new(attrs: &[AttributeType]) -> Self {
381        Self {
382            attributes: attrs.to_vec(),
383        }
384    }
385
386    /// Add an [`AttributeType`] that is unsupported
387    ///
388    /// # Examples
389    ///
390    /// ```
391    /// # use stun_types::attribute::*;
392    /// let mut unknown = UnknownAttributes::new(&[]);
393    /// unknown.add_attribute(Username::TYPE);
394    /// assert!(unknown.has_attribute(Username::TYPE));
395    /// ```
396    pub fn add_attribute(&mut self, attr: AttributeType) {
397        if !self.has_attribute(attr) {
398            self.attributes.push(attr);
399        }
400    }
401
402    /// Check if an [`AttributeType`] is present
403    ///
404    /// # Examples
405    ///
406    /// ```
407    /// # use stun_types::attribute::*;
408    /// let unknown = UnknownAttributes::new(&[Username::TYPE]);
409    /// assert!(unknown.has_attribute(Username::TYPE));
410    /// assert!(!unknown.has_attribute(ErrorCode::TYPE));
411    /// ```
412    pub fn has_attribute(&self, attr: AttributeType) -> bool {
413        self.attributes.contains(&attr)
414    }
415
416    fn write_into_data(&self, dest: &mut [u8]) -> usize {
417        let mut offset = 0;
418        for attr in &self.attributes {
419            BigEndian::write_u16(&mut dest[offset..offset + 2], (*attr).into());
420            offset += 2;
421        }
422        offset
423    }
424}
425
426impl core::fmt::Display for UnknownAttributes {
427    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
428        write!(f, "{}: {:?}", Self::TYPE, self.attributes)
429    }
430}
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435    use crate::attribute::{AlternateServer, Nonce, Realm};
436    use tracing::trace;
437
438    const CODES: [u16; 17] = [
439        300, 301, 400, 401, 403, 420, 437, 438, 440, 441, 442, 443, 486, 487, 500, 508, 699,
440    ];
441
442    #[test]
443    fn error_code() {
444        let _log = crate::tests::test_init_log();
445        for code in CODES {
446            let reason = ErrorCode::default_reason_for_code(code);
447            let err = ErrorCode::new(code, reason).unwrap();
448            trace!("{err}");
449            assert_eq!(err.code(), code);
450            assert_eq!(err.reason(), reason);
451        }
452    }
453
454    #[test]
455    fn error_code_raw() {
456        let _log = crate::tests::test_init_log();
457        for code in CODES {
458            let reason = ErrorCode::default_reason_for_code(code);
459            let err = ErrorCode::new(code, reason).unwrap();
460            let raw = RawAttribute::from(&err);
461            trace!("{raw}");
462            assert_eq!(raw.get_type(), ErrorCode::TYPE);
463            let err2 = ErrorCode::try_from(&raw).unwrap();
464            assert_eq!(err2.code(), code);
465            assert_eq!(err2.reason(), reason);
466        }
467    }
468
469    #[test]
470    fn error_code_write_into() {
471        let _log = crate::tests::test_init_log();
472        for code in CODES {
473            let reason = ErrorCode::default_reason_for_code(code);
474            let err = ErrorCode::new(code, reason).unwrap();
475            let raw = RawAttribute::from(&err);
476            let mut dest = vec![0; raw.padded_len()];
477            err.write_into(&mut dest).unwrap();
478            let raw = RawAttribute::from_bytes(&dest).unwrap();
479            let err2 = ErrorCode::try_from(&raw).unwrap();
480            assert_eq!(err2.code(), code);
481            assert_eq!(err2.reason(), reason);
482        }
483    }
484
485    #[test]
486    #[should_panic(expected = "out of range")]
487    fn error_code_write_into_unchecked() {
488        let _log = crate::tests::test_init_log();
489        let reason = ErrorCode::default_reason_for_code(CODES[0]);
490        let err = ErrorCode::new(CODES[0], reason).unwrap();
491        let raw = RawAttribute::from(&err);
492        let mut dest = vec![0; raw.padded_len() - 1];
493        err.write_into_unchecked(&mut dest);
494    }
495
496    fn error_code_new(code: u16) -> ErrorCode {
497        let reason = ErrorCode::default_reason_for_code(code);
498        ErrorCode::new(code, reason).unwrap()
499    }
500
501    #[test]
502    fn error_code_parse_short() {
503        let _log = crate::tests::test_init_log();
504        let err = error_code_new(420);
505        let raw = RawAttribute::from(&err);
506        // no data
507        let mut data: Vec<_> = raw.into();
508        let len = 0;
509        BigEndian::write_u16(&mut data[2..4], len as u16);
510        assert!(matches!(
511            ErrorCode::try_from(&RawAttribute::from_bytes(data[..len + 4].as_ref()).unwrap()),
512            Err(StunParseError::Truncated {
513                expected: 4,
514                actual: 0
515            })
516        ));
517    }
518
519    #[test]
520    fn error_code_parse_wrong_implementation() {
521        let _log = crate::tests::test_init_log();
522        let err = error_code_new(420);
523        let raw = RawAttribute::from(&err);
524        // provide incorrectly typed data
525        let mut data: Vec<_> = raw.into();
526        BigEndian::write_u16(&mut data[0..2], 0);
527        assert!(matches!(
528            ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
529            Err(StunParseError::WrongAttributeImplementation)
530        ));
531    }
532
533    #[test]
534    fn error_code_parse_out_of_range_code() {
535        let _log = crate::tests::test_init_log();
536        let err = error_code_new(420);
537        let raw = RawAttribute::from(&err);
538        let mut data: Vec<_> = raw.into();
539
540        // write an invalid error code
541        data[6] = 7;
542        assert!(matches!(
543            ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
544            Err(StunParseError::InvalidAttributeData)
545        ));
546    }
547
548    #[test]
549    fn error_code_parse_invalid_reason() {
550        let _log = crate::tests::test_init_log();
551        let err = error_code_new(420);
552        let raw = RawAttribute::from(&err);
553        let mut data: Vec<_> = raw.into();
554
555        // write an invalid utf8 bytes
556        data[10] = 0x88;
557        assert!(matches!(
558            ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
559            Err(StunParseError::InvalidAttributeData)
560        ));
561    }
562
563    #[test]
564    fn error_code_build_default_reason() {
565        let _log = crate::tests::test_init_log();
566        let err = ErrorCode::builder(420).build().unwrap();
567        assert_eq!(err.code(), 420);
568        assert!(!err.reason().is_empty());
569    }
570
571    #[test]
572    fn error_code_build_out_of_range() {
573        let _log = crate::tests::test_init_log();
574        assert!(matches!(
575            ErrorCode::builder(700).build(),
576            Err(StunWriteError::OutOfRange {
577                value: 700,
578                min: _,
579                max: _
580            })
581        ));
582    }
583
584    #[test]
585    fn error_code_new_out_of_range() {
586        let _log = crate::tests::test_init_log();
587        assert!(matches!(
588            ErrorCode::new(700, "some-reason"),
589            Err(StunWriteError::OutOfRange {
590                value: 700,
591                min: _,
592                max: _
593            })
594        ));
595    }
596
597    #[test]
598    fn unknown_attributes() {
599        let _log = crate::tests::test_init_log();
600        let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
601        unknown.add_attribute(AlternateServer::TYPE);
602        // duplicates ignored
603        unknown.add_attribute(AlternateServer::TYPE);
604        trace!("{unknown}");
605        assert!(unknown.has_attribute(Realm::TYPE));
606        assert!(unknown.has_attribute(AlternateServer::TYPE));
607        assert!(!unknown.has_attribute(Nonce::TYPE));
608    }
609
610    #[test]
611    fn unknown_attributes_raw() {
612        let _log = crate::tests::test_init_log();
613        let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
614        unknown.add_attribute(AlternateServer::TYPE);
615        let raw = RawAttribute::from(&unknown);
616        assert_eq!(raw.get_type(), UnknownAttributes::TYPE);
617        let unknown2 = UnknownAttributes::try_from(&raw).unwrap();
618        assert!(unknown2.has_attribute(Realm::TYPE));
619        assert!(unknown2.has_attribute(AlternateServer::TYPE));
620        assert!(!unknown2.has_attribute(Nonce::TYPE));
621    }
622
623    #[test]
624    fn unknown_attributes_raw_short() {
625        let _log = crate::tests::test_init_log();
626        let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
627        unknown.add_attribute(AlternateServer::TYPE);
628        let raw = RawAttribute::from(&unknown);
629        // truncate by one byte
630        let mut data: Vec<_> = raw.clone().into();
631        let len = data.len();
632        BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
633        assert!(matches!(
634            UnknownAttributes::try_from(
635                &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
636            ),
637            Err(StunParseError::Truncated {
638                expected: 4,
639                actual: 3
640            })
641        ));
642    }
643
644    #[test]
645    fn unknown_attributes_raw_wrong_type() {
646        let _log = crate::tests::test_init_log();
647        let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
648        unknown.add_attribute(AlternateServer::TYPE);
649        let raw = RawAttribute::from(&unknown);
650        // provide incorrectly typed data
651        let mut data: Vec<_> = raw.clone().into();
652        BigEndian::write_u16(&mut data[0..2], 0);
653        assert!(matches!(
654            UnknownAttributes::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
655            Err(StunParseError::WrongAttributeImplementation)
656        ));
657    }
658
659    #[test]
660    fn unknown_attributes_write_into() {
661        let _log = crate::tests::test_init_log();
662        let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
663        unknown.add_attribute(AlternateServer::TYPE);
664        let raw = RawAttribute::from(&unknown);
665
666        let mut dest = vec![0; raw.padded_len()];
667        unknown.write_into(&mut dest).unwrap();
668        tracing::error!("{dest:?}");
669        let raw = RawAttribute::from_bytes(&dest).unwrap();
670        let unknown2 = UnknownAttributes::try_from(&raw).unwrap();
671        assert!(unknown2.has_attribute(Realm::TYPE));
672        assert!(unknown2.has_attribute(AlternateServer::TYPE));
673    }
674
675    #[test]
676    #[should_panic(expected = "out of range")]
677    fn unknown_attributes_write_into_unchecked() {
678        let _log = crate::tests::test_init_log();
679        let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
680        unknown.add_attribute(AlternateServer::TYPE);
681        let raw = RawAttribute::from(&unknown);
682
683        let mut dest = vec![0; raw.padded_len() - 1];
684        unknown.write_into_unchecked(&mut dest);
685    }
686}