Skip to main content

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