stun_types/attribute/
mod.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//! STUN Attributes
10//!
11//! Provides implementations for generating, parsing and manipulating STUN attributes as specified
12//! in one of [RFC8489], [RFC5389], or [RFC3489].
13//!
14//! [RFC8489]: https://tools.ietf.org/html/rfc8489
15//! [RFC5389]: https://tools.ietf.org/html/rfc5389
16//! [RFC3489]: https://tools.ietf.org/html/rfc3489
17//!
18//! # Examples
19//!
20//! ### Parse and write an already defined [`Attribute`]
21//!
22//! ```
23//! # use stun_types::prelude::*;
24//! use stun_types::attribute::{RawAttribute, Software};
25//! let software_name = "stun-types";
26//! let software = Software::new(software_name).unwrap();
27//! assert_eq!(software.software(), software_name);
28//!
29//! let attribute_data = [
30//!     0x80, 0x22, 0x00, 0x0a, // Attribute type (0x8022: Software) and length (0x000a)
31//!     0x73, 0x74, 0x75, 0x6E, // s t u n
32//!     0x2D, 0x74, 0x79, 0x70, // - t y p
33//!     0x65, 0x73, 0x00, 0x00  // e s
34//! ];
35//!
36//! let raw = RawAttribute::from(&software);
37//! assert_eq!(raw.to_bytes(), attribute_data);
38//!
39//! // Can also parse data into a typed attribute as needed
40//! let software = Software::from_raw(raw).unwrap();
41//! assert_eq!(software.software(), software_name);
42//! ```
43//!
44//! ### Defining your own [`Attribute`]
45//!
46//! ```
47//! # use stun_types::prelude::*;
48//! use byteorder::{BigEndian, ByteOrder};
49//! use stun_types::attribute::{AttributeType, RawAttribute};
50//! use stun_types::message::StunParseError;
51//! #[derive(Debug)]
52//! struct MyAttribute {
53//!    value: u32,
54//! }
55//! impl AttributeStaticType for MyAttribute {
56//!    const TYPE: AttributeType = AttributeType::new(0x8851);
57//! }
58//! impl Attribute for MyAttribute {
59//!    fn get_type(&self) -> AttributeType {
60//!        Self::TYPE
61//!    }
62//!
63//!    fn length(&self) -> u16 {
64//!        4
65//!    }
66//! }
67//! impl AttributeWrite for MyAttribute {
68//!     fn to_raw(&self) -> RawAttribute {
69//!         let mut ret = [0; 4];
70//!         BigEndian::write_u32(&mut ret, self.value);
71//!         RawAttribute::new(MyAttribute::TYPE, &ret).into_owned()
72//!     }
73//!     fn write_into_unchecked(&self, dest: &mut [u8]) {
74//!         self.write_header_unchecked(dest);
75//!         BigEndian::write_u32(&mut dest[4..], self.value);
76//!     }
77//! }
78//! impl<'a> AttributeFromRaw<'a> for MyAttribute {
79//!     fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
80//!     where
81//!         Self: Sized,
82//!     {
83//!         raw.check_type_and_len(Self::TYPE, 4..=4)?;
84//!         let value = BigEndian::read_u32(&raw.value);
85//!         Ok(Self {
86//!             value,
87//!         })
88//!     }
89//! }
90//!
91//! let my_attr = MyAttribute { value: 0x4729 };
92//! let raw = RawAttribute::from(&my_attr);
93//!
94//! let attribute_data = [
95//!     0x88, 0x51, 0x00, 0x04,
96//!     0x00, 0x00, 0x47, 0x29,
97//! ];
98//! assert_eq!(raw.to_bytes(), attribute_data);
99//!
100//! let my_attr = MyAttribute::from_raw(raw).unwrap();
101//! assert_eq!(my_attr.value, 0x4729);
102//! ```
103
104macro_rules! bytewise_xor {
105    ($size:literal, $a:expr, $b:expr, $default:literal) => {{
106        let mut arr = [$default; $size];
107        for (i, item) in arr.iter_mut().enumerate() {
108            *item = $a[i] ^ $b[i];
109        }
110        arr
111    }};
112}
113
114mod address;
115pub use address::{AddressFamily, MappedSocketAddr, XorSocketAddr};
116mod alternate;
117pub use alternate::{AlternateDomain, AlternateServer};
118mod error;
119pub use error::{ErrorCode, UnknownAttributes};
120mod ice;
121pub use ice::{IceControlled, IceControlling, Priority, UseCandidate};
122mod integrity;
123pub use integrity::{MessageIntegrity, MessageIntegritySha256};
124mod fingerprint;
125pub use fingerprint::Fingerprint;
126mod nonce;
127pub use nonce::Nonce;
128mod password_algorithm;
129pub use password_algorithm::{PasswordAlgorithm, PasswordAlgorithmValue, PasswordAlgorithms};
130mod realm;
131pub use realm::Realm;
132mod user;
133pub use user::{Userhash, Username};
134mod software;
135pub use software::Software;
136mod xor_addr;
137pub use xor_addr::XorMappedAddress;
138
139use crate::data::Data;
140use crate::message::{StunParseError, StunWriteError};
141
142use byteorder::{BigEndian, ByteOrder};
143
144/// The type of an [`Attribute`] in a STUN [`Message`](crate::message::Message)
145#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
146pub struct AttributeType(u16);
147
148impl std::fmt::Display for AttributeType {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        write!(f, "{}({:#x}: {})", self.0, self.0, self.name())
151    }
152}
153
154impl AttributeType {
155    /// Create a new AttributeType from an existing value
156    ///
157    /// Note: the value passed in is not encoded as in a stun message
158    ///
159    /// # Examples
160    /// ```
161    /// # use stun_types::attribute::AttributeType;
162    /// assert_eq!(AttributeType::new(0x123).value(), 0x123);
163    /// ```
164    pub const fn new(val: u16) -> Self {
165        Self(val)
166    }
167
168    /// Return the integer value of this AttributeType
169    ///
170    /// Note: the value returned is not encoded as in a stun message
171    ///
172    /// # Examples
173    /// ```
174    /// # use stun_types::attribute::AttributeType;
175    /// assert_eq!(AttributeType::new(0x123).value(), 0x123);
176    /// ```
177    pub fn value(&self) -> u16 {
178        self.0
179    }
180
181    /// Returns a human readable name of this `AttributeType` or "unknown"
182    ///
183    /// # Examples
184    /// ```
185    /// # use stun_types::attribute::*;
186    /// assert_eq!(XorMappedAddress::TYPE.name(), "XOR-MAPPED-ADDRESS");
187    /// ```
188    pub fn name(self) -> &'static str {
189        match self {
190            AttributeType(0x0001) => "MAPPED-ADDRESS",
191            Username::TYPE => "USERNAME",
192            MessageIntegrity::TYPE => "MESSAGE-INTEGRITY",
193            ErrorCode::TYPE => "ERROR-CODE",
194            UnknownAttributes::TYPE => "UNKNOWN-ATTRIBUTES",
195            Realm::TYPE => "REALM",
196            Nonce::TYPE => "NONCE",
197            MessageIntegritySha256::TYPE => "MESSAGE-INTEGRITY-SHA256",
198            PasswordAlgorithm::TYPE => "PASSWORD-ALGORITHM",
199            Userhash::TYPE => "USERHASH",
200            XorMappedAddress::TYPE => "XOR-MAPPED-ADDRESS",
201            PasswordAlgorithms::TYPE => "PASSWORD_ALGORITHMS",
202            AlternateDomain::TYPE => "ALTERNATE-DOMAIN",
203            Software::TYPE => "SOFTWARE",
204            AlternateServer::TYPE => "ALTERNATE-SERVER",
205            Fingerprint::TYPE => "FINGERPRINT",
206            Priority::TYPE => "PRIORITY",
207            UseCandidate::TYPE => "USE-CANDIDATE",
208            IceControlled::TYPE => "ICE-CONTROLLED",
209            IceControlling::TYPE => "ICE-CONTROLLING",
210            _ => "unknown",
211        }
212    }
213
214    /// Check if comprehension is required for an `AttributeType`.  All integer attribute
215    /// values < 0x8000 require comprehension.
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// # use stun_types::attribute::AttributeType;
221    /// assert_eq!(AttributeType::new(0x0).comprehension_required(), true);
222    /// assert_eq!(AttributeType::new(0x8000).comprehension_required(), false);
223    /// ```
224    pub fn comprehension_required(self) -> bool {
225        self.0 < 0x8000
226    }
227}
228impl From<u16> for AttributeType {
229    fn from(f: u16) -> Self {
230        Self::new(f)
231    }
232}
233impl From<AttributeType> for u16 {
234    fn from(f: AttributeType) -> Self {
235        f.0
236    }
237}
238
239/// Structure for holding the header of a STUN attribute.  Contains the type and the length
240#[derive(Debug, Copy, Clone, PartialEq, Eq)]
241pub struct AttributeHeader {
242    atype: AttributeType,
243    length: u16,
244}
245
246impl AttributeHeader {
247    fn parse(data: &[u8]) -> Result<Self, StunParseError> {
248        if data.len() < 4 {
249            return Err(StunParseError::Truncated {
250                expected: 4,
251                actual: data.len(),
252            });
253        }
254        let ret = Self {
255            atype: BigEndian::read_u16(&data[0..2]).into(),
256            length: BigEndian::read_u16(&data[2..4]),
257        };
258        Ok(ret)
259    }
260
261    fn to_bytes(self) -> Vec<u8> {
262        let mut ret = vec![0; 4];
263        self.write_into(&mut ret);
264        ret
265    }
266
267    fn write_into(&self, ret: &mut [u8]) {
268        BigEndian::write_u16(&mut ret[0..2], self.atype.into());
269        BigEndian::write_u16(&mut ret[2..4], self.length);
270    }
271
272    /// Returns the type of the attribute
273    pub fn get_type(&self) -> AttributeType {
274        self.atype
275    }
276
277    /// Returns the length of the attribute
278    pub fn length(&self) -> u16 {
279        self.length
280    }
281}
282impl From<AttributeHeader> for Vec<u8> {
283    fn from(f: AttributeHeader) -> Self {
284        f.to_bytes()
285    }
286}
287impl TryFrom<&[u8]> for AttributeHeader {
288    type Error = StunParseError;
289
290    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
291        AttributeHeader::parse(value)
292    }
293}
294
295/// A static type for an [`Attribute`]
296pub trait AttributeStaticType {
297    /// The [`AttributeType`]
298    const TYPE: AttributeType;
299}
300
301/// A STUN attribute for use in [`Message`](crate::message::Message)s
302pub trait Attribute: std::fmt::Debug + std::marker::Sync {
303    /// Retrieve the type of an `Attribute`.
304    fn get_type(&self) -> AttributeType;
305
306    /// Retrieve the length of an `Attribute`.  This is not the padded length as stored in a
307    /// `Message` and does not include the size of the attribute header.
308    fn length(&self) -> u16;
309}
310
311/// A trait for converting to a concrete [`Attribute`] from a [`RawAttribute`]
312pub trait AttributeFromRaw<'a>: Attribute {
313    /// Convert an `Attribute` from a `RawAttribute`
314    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
315    where
316        Self: Sized;
317
318    /// Convert an `Attribute` from a `RawAttribute`
319    fn from_raw(raw: RawAttribute<'a>) -> Result<Self, StunParseError>
320    where
321        Self: Sized,
322    {
323        Self::from_raw_ref(&raw)
324    }
325}
326
327fn padded_attr_len(len: usize) -> usize {
328    if len % 4 == 0 {
329        len
330    } else {
331        len + 4 - len % 4
332    }
333}
334
335/// Automatically implemented trait providing some helper functions.
336pub trait AttributeExt {
337    /// The length in bytes of an attribute as stored in a [`Message`](crate::message::Message)
338    /// including any padding and the attribute header.
339    fn padded_len(&self) -> usize;
340}
341
342impl<A: Attribute + ?Sized> AttributeExt for A {
343    fn padded_len(&self) -> usize {
344        4 + padded_attr_len(self.length() as usize)
345    }
346}
347
348/// Trait required when implementing writing an [`Attribute`] to a sequence of bytes
349pub trait AttributeWrite: Attribute {
350    /// Write attribute to the provided destination buffer.
351    ///
352    /// Panics if the destination buffer is not large enough
353    fn write_into_unchecked(&self, dest: &mut [u8]);
354    /// Produce a [`RawAttribute`] from this [`Attribute`]
355    fn to_raw(&self) -> RawAttribute;
356}
357
358/// Automatically implemented trait providing helper functionality for writing an [`Attribute`] to
359/// a sequence of bytes.
360pub trait AttributeWriteExt: AttributeWrite {
361    /// Write the 4 byte attribute header into the provided destination buffer returning the
362    /// number of bytes written.
363    ///
364    /// Panics if the destination cannot hold at least 4 bytes of data.
365    fn write_header_unchecked(&self, dest: &mut [u8]) -> usize;
366    /// Write the 4 byte attribute header into the provided destination buffer returning the
367    /// number of bytes written, or an error.
368    fn write_header(&self, dest: &mut [u8]) -> Result<usize, StunWriteError>;
369    /// Write this attribute into the provided destination buffer returning the number of bytes
370    /// written, or an error.
371    fn write_into(&self, dest: &mut [u8]) -> Result<usize, StunWriteError>;
372}
373
374impl<A: AttributeWrite + ?Sized> AttributeWriteExt for A {
375    fn write_header(&self, dest: &mut [u8]) -> Result<usize, StunWriteError> {
376        if dest.len() < 4 {
377            return Err(StunWriteError::TooSmall {
378                expected: 4,
379                actual: dest.len(),
380            });
381        }
382        self.write_header_unchecked(dest);
383        Ok(4)
384    }
385    fn write_header_unchecked(&self, dest: &mut [u8]) -> usize {
386        AttributeHeader {
387            atype: self.get_type(),
388            length: self.length(),
389        }
390        .write_into(dest);
391        4
392    }
393
394    fn write_into(&self, dest: &mut [u8]) -> Result<usize, StunWriteError> {
395        let len = self.padded_len();
396        if len > dest.len() {
397            return Err(StunWriteError::TooSmall {
398                expected: len,
399                actual: dest.len(),
400            });
401        }
402        self.write_into_unchecked(dest);
403        Ok(len)
404    }
405}
406
407/// The header and raw bytes of an unparsed [`Attribute`]
408#[derive(Debug, Clone, PartialEq, Eq)]
409pub struct RawAttribute<'a> {
410    /// The [`AttributeHeader`] of this [`RawAttribute`]
411    pub header: AttributeHeader,
412    /// The raw bytes of this [`RawAttribute`]
413    pub value: Data<'a>,
414}
415
416macro_rules! display_attr {
417    ($this:ident, $CamelType:ty, $default:ident) => {{
418        if let Ok(attr) = <$CamelType>::from_raw_ref($this) {
419            format!("{}", attr)
420        } else {
421            $default
422        }
423    }};
424}
425
426impl std::fmt::Display for RawAttribute<'_> {
427    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
428        // try to get a more specialised display
429        let malformed_str = format!(
430            "{}(Malformed): len: {}, data: {:?})",
431            self.get_type(),
432            self.header.length(),
433            self.value
434        );
435        let display_str = match self.get_type() {
436            Username::TYPE => display_attr!(self, Username, malformed_str),
437            MessageIntegrity::TYPE => display_attr!(self, MessageIntegrity, malformed_str),
438            ErrorCode::TYPE => display_attr!(self, ErrorCode, malformed_str),
439            UnknownAttributes::TYPE => display_attr!(self, UnknownAttributes, malformed_str),
440            Realm::TYPE => display_attr!(self, Realm, malformed_str),
441            Nonce::TYPE => display_attr!(self, Nonce, malformed_str),
442            MessageIntegritySha256::TYPE => {
443                display_attr!(self, MessageIntegritySha256, malformed_str)
444            }
445            PasswordAlgorithm::TYPE => display_attr!(self, PasswordAlgorithm, malformed_str),
446            //UserHash::TYPE => display_attr!(self, UserHash, malformed_str),
447            XorMappedAddress::TYPE => display_attr!(self, XorMappedAddress, malformed_str),
448            PasswordAlgorithms::TYPE => display_attr!(self, PasswordAlgorithms, malformed_str),
449            AlternateDomain::TYPE => display_attr!(self, AlternateDomain, malformed_str),
450            Software::TYPE => display_attr!(self, Software, malformed_str),
451            AlternateServer::TYPE => display_attr!(self, AlternateServer, malformed_str),
452            Fingerprint::TYPE => display_attr!(self, Fingerprint, malformed_str),
453            Priority::TYPE => display_attr!(self, Priority, malformed_str),
454            UseCandidate::TYPE => display_attr!(self, UseCandidate, malformed_str),
455            IceControlled::TYPE => display_attr!(self, IceControlled, malformed_str),
456            IceControlling::TYPE => display_attr!(self, IceControlling, malformed_str),
457            _ => format!(
458                "RawAttribute (type: {:?}, len: {}, data: {:?})",
459                self.header.get_type(),
460                self.header.length(),
461                &self.value
462            ),
463        };
464        write!(f, "{}", display_str)
465    }
466}
467
468impl<'a> RawAttribute<'a> {
469    /// Create a new [`RawAttribute`]
470    pub fn new(atype: AttributeType, data: &'a [u8]) -> Self {
471        Self {
472            header: AttributeHeader {
473                atype,
474                length: data.len() as u16,
475            },
476            value: data.into(),
477        }
478    }
479
480    /// Create a new owned [`RawAttribute`]
481    pub fn new_owned(atype: AttributeType, data: Box<[u8]>) -> Self {
482        Self {
483            header: AttributeHeader {
484                atype,
485                length: data.len() as u16,
486            },
487            value: data.into(),
488        }
489    }
490
491    /// Deserialize a `RawAttribute` from bytes.
492    ///
493    /// # Examples
494    ///
495    /// ```
496    /// # use stun_types::attribute::{RawAttribute, Attribute, AttributeType};
497    /// let data = &[0, 1, 0, 2, 5, 6, 0, 0];
498    /// let attr = RawAttribute::from_bytes(data).unwrap();
499    /// assert_eq!(attr.get_type(), AttributeType::new(1));
500    /// assert_eq!(attr.length(), 2);
501    /// ```
502    pub fn from_bytes(data: &'a [u8]) -> Result<Self, StunParseError> {
503        let header = AttributeHeader::parse(data)?;
504        // the advertised length is larger than actual data -> error
505        if header.length() > (data.len() - 4) as u16 {
506            return Err(StunParseError::Truncated {
507                expected: header.length() as usize,
508                actual: data.len() - 4,
509            });
510        }
511        Ok(Self {
512            header,
513            value: Data::Borrowed(data[4..header.length() as usize + 4].into()),
514        })
515    }
516
517    /// Serialize a `RawAttribute` to bytes.
518    ///
519    /// # Examples
520    ///
521    /// ```
522    /// # use stun_types::attribute::{RawAttribute, Attribute, AttributeType};
523    /// let attr = RawAttribute::new(AttributeType::new(1), &[5, 6]);
524    /// assert_eq!(attr.to_bytes(), &[0, 1, 0, 2, 5, 6, 0, 0]);
525    /// ```
526    pub fn to_bytes(&self) -> Vec<u8> {
527        let mut vec = Vec::with_capacity(self.padded_len());
528        let mut header_bytes = [0; 4];
529        self.header.write_into(&mut header_bytes);
530        vec.extend(&header_bytes);
531        vec.extend(&*self.value);
532        let len = vec.len();
533        if len % 4 != 0 {
534            // pad to 4 bytes
535            vec.resize(len + 4 - (len % 4), 0);
536        }
537        vec
538    }
539
540    /// Helper for checking that a raw attribute is of a particular type and within a certain range
541    pub fn check_type_and_len(
542        &self,
543        atype: AttributeType,
544        allowed_range: impl std::ops::RangeBounds<usize>,
545    ) -> Result<(), StunParseError> {
546        if self.header.get_type() != atype {
547            return Err(StunParseError::WrongAttributeImplementation);
548        }
549        check_len(self.value.len(), allowed_range)
550    }
551
552    /// Consume this [`RawAttribute`] and return a new owned [`RawAttribute`]
553    pub fn into_owned<'b>(self) -> RawAttribute<'b> {
554        RawAttribute {
555            header: self.header,
556            value: self.value.into_owned(),
557        }
558    }
559}
560
561impl Attribute for RawAttribute<'_> {
562    /// Returns the [`AttributeType`] of this [`RawAttribute`]
563    fn get_type(&self) -> AttributeType {
564        self.header.get_type()
565    }
566
567    /// Returns the length of this [`RawAttribute`]
568    fn length(&self) -> u16 {
569        self.value.len() as u16
570    }
571}
572
573impl<'a> AttributeWrite for RawAttribute<'a> {
574    /// Write this [`RawAttribute`] into a byte slice.  Returns the number of bytes written.
575    fn write_into_unchecked(&self, dest: &mut [u8]) {
576        let len = self.padded_len();
577        self.header.write_into(dest);
578        let mut offset = 4;
579        dest[offset..offset + self.value.len()].copy_from_slice(&self.value);
580        offset += self.value.len();
581        if len - offset > 0 {
582            dest[offset..len].fill(0);
583        }
584    }
585
586    fn to_raw(&self) -> RawAttribute<'a> {
587        self.clone()
588    }
589}
590
591impl<'a, A: AttributeWrite> From<&'a A> for RawAttribute<'a> {
592    fn from(value: &'a A) -> Self {
593        value.to_raw()
594    }
595}
596
597fn check_len(
598    len: usize,
599    allowed_range: impl std::ops::RangeBounds<usize>,
600) -> Result<(), StunParseError> {
601    match allowed_range.start_bound() {
602        std::ops::Bound::Unbounded => (),
603        std::ops::Bound::Included(start) => {
604            if len < *start {
605                return Err(StunParseError::Truncated {
606                    expected: *start,
607                    actual: len,
608                });
609            }
610        }
611        std::ops::Bound::Excluded(start) => {
612            if len <= *start {
613                return Err(StunParseError::Truncated {
614                    expected: start + 1,
615                    actual: len,
616                });
617            }
618        }
619    }
620    match allowed_range.end_bound() {
621        std::ops::Bound::Unbounded => (),
622        std::ops::Bound::Included(end) => {
623            if len > *end {
624                return Err(StunParseError::TooLarge {
625                    expected: *end,
626                    actual: len,
627                });
628            }
629        }
630        std::ops::Bound::Excluded(end) => {
631            if len >= *end {
632                return Err(StunParseError::TooLarge {
633                    expected: *end - 1,
634                    actual: len,
635                });
636            }
637        }
638    }
639    Ok(())
640}
641
642impl From<RawAttribute<'_>> for Vec<u8> {
643    fn from(f: RawAttribute) -> Self {
644        f.to_bytes()
645    }
646}
647
648#[cfg(test)]
649mod tests {
650    use super::*;
651
652    #[test]
653    fn attribute_type() {
654        let _log = crate::tests::test_init_log();
655        let atype = ErrorCode::TYPE;
656        let anum: u16 = atype.into();
657        assert_eq!(atype, anum.into());
658    }
659
660    #[test]
661    fn short_attribute_header() {
662        let _log = crate::tests::test_init_log();
663        let data = [0; 1];
664        // not enough data to parse the header
665        let res: Result<AttributeHeader, _> = data.as_ref().try_into();
666        assert!(res.is_err());
667    }
668
669    #[test]
670    fn raw_attribute_construct() {
671        let _log = crate::tests::test_init_log();
672        let a = RawAttribute::new(1.into(), &[80, 160]);
673        assert_eq!(a.get_type(), 1.into());
674        let bytes: Vec<_> = a.into();
675        assert_eq!(bytes, &[0, 1, 0, 2, 80, 160, 0, 0]);
676        let b = RawAttribute::from_bytes(bytes.as_ref()).unwrap();
677        assert_eq!(b.get_type(), 1.into());
678    }
679
680    #[test]
681    fn raw_attribute_encoding() {
682        let _log = crate::tests::test_init_log();
683        let orig = RawAttribute::new(1.into(), &[80, 160]);
684        assert_eq!(orig.get_type(), 1.into());
685        let mut data: Vec<_> = orig.into();
686        let len = data.len();
687        // one byte too big vs data size
688        BigEndian::write_u16(&mut data[2..4], len as u16 - 4 + 1);
689        assert!(matches!(
690            RawAttribute::from_bytes(data.as_ref()),
691            Err(StunParseError::Truncated {
692                expected: 5,
693                actual: 4
694            })
695        ));
696    }
697
698    #[test]
699    fn test_check_len() {
700        let _log = crate::tests::test_init_log();
701        assert!(check_len(4, ..).is_ok());
702        assert!(check_len(4, 0..).is_ok());
703        assert!(check_len(4, 0..8).is_ok());
704        assert!(check_len(4, 0..=8).is_ok());
705        assert!(check_len(4, ..=8).is_ok());
706        assert!(matches!(
707            check_len(4, ..4),
708            Err(StunParseError::TooLarge {
709                expected: 3,
710                actual: 4
711            })
712        ));
713        assert!(matches!(
714            check_len(4, 5..),
715            Err(StunParseError::Truncated {
716                expected: 5,
717                actual: 4
718            })
719        ));
720        assert!(matches!(
721            check_len(4, ..=3),
722            Err(StunParseError::TooLarge {
723                expected: 3,
724                actual: 4
725            })
726        ));
727        assert!(matches!(
728            check_len(
729                4,
730                (std::ops::Bound::Excluded(4), std::ops::Bound::Unbounded)
731            ),
732            Err(StunParseError::Truncated {
733                expected: 5,
734                actual: 4
735            })
736        ));
737    }
738}