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