Skip to main content

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// SPDX-License-Identifier: MIT OR Apache-2.0
10
11//! STUN Attributes
12//!
13//! Provides implementations for generating, parsing and manipulating STUN attributes as specified
14//! in one of [RFC8489], [RFC5389], or [RFC3489].
15//!
16//! There are two levels of attribute implementations:
17//! 1. A generic [`RawAttribute`] which contains the [`AttributeHeader`] (type and length) and the
18//!    byte sequence of data (either borrowed or owned). Parsing a
19//!    [`Message`](crate::message::Message) will only perform zerocopy parsing to this level. Any
20//!    attribute-specific restrictions on the actual contents of the data should be performed by
21//!    concrete attribute implementations.
22//! 2. Concrete implementations based on implementing [`Attribute`], and [`AttributeStaticType`].
23//!    These concrete attribute implementations have much more ergonomic API specific to their
24//!    particular needs. A concrete attribute implementation may have restrictions on what data is
25//!    allowed to be parsed from a [`RawAttribute`] that should return errors when calling
26//!    [`AttributeFromRaw::from_raw_ref`].
27//!
28//! [RFC8489]: https://tools.ietf.org/html/rfc8489
29//! [RFC5389]: https://tools.ietf.org/html/rfc5389
30//! [RFC3489]: https://tools.ietf.org/html/rfc3489
31//!
32//! # Examples
33//!
34//! ### Parse and write an already defined [`Attribute`]
35//!
36//! ```
37//! # use stun_types::prelude::*;
38//! use stun_types::attribute::{RawAttribute, Software};
39//! let software_name = "stun-types";
40//! let software = Software::new(software_name).unwrap();
41//! assert_eq!(software.software(), software_name);
42//!
43//! let attribute_data = [
44//!     0x80, 0x22, 0x00, 0x0a, // Attribute type (0x8022: Software) and length (0x000a)
45//!     0x73, 0x74, 0x75, 0x6E, // s t u n
46//!     0x2D, 0x74, 0x79, 0x70, // - t y p
47//!     0x65, 0x73, 0x00, 0x00  // e s
48//! ];
49//!
50//! let raw = RawAttribute::from(&software);
51//! assert_eq!(raw.to_bytes(), attribute_data);
52//!
53//! // Can also parse data into a typed attribute as needed
54//! let software = Software::from_raw(raw).unwrap();
55//! assert_eq!(software.software(), software_name);
56//! ```
57//!
58//! ### Defining your own [`Attribute`]
59//!
60//! ```
61//! # use stun_types::prelude::*;
62//! use byteorder::{BigEndian, ByteOrder};
63//! use stun_types::attribute::{AttributeType, RawAttribute};
64//! use stun_types::message::StunParseError;
65//! #[derive(Debug)]
66//! struct MyAttribute {
67//!    value: u32,
68//! }
69//! impl AttributeStaticType for MyAttribute {
70//!    const TYPE: AttributeType = AttributeType::new(0x8851);
71//! }
72//! impl Attribute for MyAttribute {
73//!    fn get_type(&self) -> AttributeType {
74//!        Self::TYPE
75//!    }
76//!
77//!    fn length(&self) -> u16 {
78//!        4
79//!    }
80//! }
81//! impl AttributeWrite for MyAttribute {
82//!     fn to_raw(&self) -> RawAttribute<'_> {
83//!         let mut ret = [0; 4];
84//!         BigEndian::write_u32(&mut ret, self.value);
85//!         RawAttribute::new(MyAttribute::TYPE, &ret).into_owned()
86//!     }
87//!     fn write_into_unchecked(&self, dest: &mut [u8]) {
88//!         self.write_header_unchecked(dest);
89//!         BigEndian::write_u32(&mut dest[4..], self.value);
90//!     }
91//! }
92//! impl AttributeFromRaw<'_> for MyAttribute {
93//!     fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
94//!     where
95//!         Self: Sized,
96//!     {
97//!         raw.check_type_and_len(Self::TYPE, 4..=4)?;
98//!         let value = BigEndian::read_u32(&raw.value);
99//!         Ok(Self {
100//!             value,
101//!         })
102//!     }
103//! }
104//!
105//! // Optional: if you want this attribute to be displayed nicely when the corresponding
106//! // `RawAttribute` (based on `AttributeType`) is formatted using `RawAttribute`'s `Display`
107//! // implementation.
108//! impl core::fmt::Display for MyAttribute {
109//!     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110//!         write!(f, "MyAttribute: {}", self.value)
111//!     }
112//! }
113//! # #[cfg(feature = "std")]
114//! stun_types::attribute_display!(MyAttribute);
115//! # #[cfg(feature = "std")]
116//! MyAttribute::TYPE.add_name("MY-ATTRIBUTE");
117//!
118//! let my_attr = MyAttribute { value: 0x4729 };
119//! let raw = RawAttribute::from(&my_attr);
120//!
121//! let attribute_data = [
122//!     0x88, 0x51, 0x00, 0x04,
123//!     0x00, 0x00, 0x47, 0x29,
124//! ];
125//! assert_eq!(raw.to_bytes(), attribute_data);
126//!
127//! let my_attr = MyAttribute::from_raw(raw).unwrap();
128//! assert_eq!(my_attr.value, 0x4729);
129//! ```
130
131macro_rules! bytewise_xor {
132    ($size:literal, $a:expr, $b:expr, $default:literal) => {{
133        let mut arr = [$default; $size];
134        for (i, item) in arr.iter_mut().enumerate() {
135            *item = $a[i] ^ $b[i];
136        }
137        arr
138    }};
139}
140
141mod address;
142pub use address::{MappedSocketAddr, XorSocketAddr};
143mod alternate;
144pub use alternate::{AlternateDomain, AlternateServer};
145mod error;
146pub use error::{ErrorCode, UnknownAttributes};
147mod integrity;
148pub use integrity::{MessageIntegrity, MessageIntegritySha256};
149mod fingerprint;
150pub use fingerprint::Fingerprint;
151mod nonce;
152pub use nonce::Nonce;
153mod password_algorithm;
154pub use password_algorithm::{PasswordAlgorithm, PasswordAlgorithmValue, PasswordAlgorithms};
155mod realm;
156pub use realm::Realm;
157mod user;
158pub use user::{Userhash, Username};
159mod software;
160pub use software::Software;
161mod xor_addr;
162pub use xor_addr::XorMappedAddress;
163
164use crate::data::Data;
165use crate::message::{StunParseError, StunWriteError};
166use alloc::boxed::Box;
167use alloc::vec::Vec;
168
169use byteorder::{BigEndian, ByteOrder};
170
171#[cfg(feature = "std")]
172use alloc::collections::BTreeMap;
173#[cfg(feature = "std")]
174use std::sync::{Mutex, OnceLock};
175
176/// A closure definition for an externally provided `Display` implementation for a [`RawAttribute`].
177///
178/// Typically, a concrete [`Attribute`] implements `Display` and
179/// [`attribute_display`](crate::attribute_display) can be used
180/// to generate and install this closure.
181///
182/// See the module level documentation for an example.
183pub type AttributeDisplay =
184    fn(&RawAttribute<'_>, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result;
185#[cfg(feature = "std")]
186static ATTRIBUTE_EXTERNAL_DISPLAY_IMPL: OnceLock<Mutex<BTreeMap<AttributeType, AttributeDisplay>>> =
187    OnceLock::new();
188
189/// Adds an externally provided Display implementation for a particular [`AttributeType`].
190///
191/// Any previous implementation is overidden.
192#[cfg(feature = "std")]
193pub fn add_display_impl(atype: AttributeType, imp: AttributeDisplay) {
194    let mut display_impls = ATTRIBUTE_EXTERNAL_DISPLAY_IMPL
195        .get_or_init(Default::default)
196        .lock()
197        .unwrap();
198    display_impls.insert(atype, imp);
199}
200
201/// Implement an [`AttributeDisplay`] closure for an [`Attribute`] from a [`RawAttribute`] and calls
202/// [`add_display_impl`] with the generated closure.
203///
204/// # Examples
205/// ```
206/// use stun_types::attribute::{AttributeType, Attribute, AttributeStaticType, AttributeFromRaw};
207/// use stun_types::attribute::RawAttribute;
208/// use stun_types::message::StunParseError;
209/// #[derive(Debug)]
210/// struct MyAttribute {}
211/// impl AttributeStaticType for MyAttribute {
212///    const TYPE: AttributeType = AttributeType::new(0x8852);
213/// }
214/// impl Attribute for MyAttribute {
215///    fn get_type(&self) -> AttributeType {
216///        Self::TYPE
217///    }
218///    fn length(&self) -> u16 {
219///        0
220///    }
221/// }
222/// impl AttributeFromRaw<'_> for MyAttribute {
223///     fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
224///     where
225///         Self: Sized,
226///     {
227///         raw.check_type_and_len(Self::TYPE, 0..=0)?;
228///         Ok(Self {})
229///    }
230/// }
231/// // An Attribute would also implement AttributeWrite but that has been omitted for brevity.
232/// impl core::fmt::Display for MyAttribute {
233///     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
234///         write!(f, "MyAttribute")
235///     }
236/// }
237/// # #[cfg(feature = "std")]
238/// # {
239/// stun_types::attribute_display!(MyAttribute);
240/// let attr = RawAttribute::new(MyAttribute::TYPE, &[]);
241/// let display_str = format!("{attr}");
242/// assert_eq!(display_str, "MyAttribute");
243/// # }
244/// ```
245#[cfg(feature = "std")]
246#[macro_export]
247macro_rules! attribute_display {
248    ($typ:ty) => {{
249        let imp = |attr: &$crate::attribute::RawAttribute<'_>,
250                   f: &mut core::fmt::Formatter<'_>|
251         -> core::fmt::Result {
252            if let Ok(attr) = <$typ>::from_raw_ref(attr) {
253                write!(f, "{}", attr)
254            } else {
255                write!(
256                    f,
257                    "{}(Malformed): len: {}, data: {:?})",
258                    attr.get_type(),
259                    attr.header.length(),
260                    attr.value
261                )
262            }
263        };
264
265        $crate::attribute::add_display_impl(<$typ>::TYPE, imp);
266    }};
267}
268
269#[cfg(feature = "std")]
270static ATTRIBUTE_TYPE_NAME_MAP: OnceLock<Mutex<BTreeMap<AttributeType, &'static str>>> =
271    OnceLock::new();
272
273/// The type of an [`Attribute`] in a STUN [`Message`](crate::message::Message).
274#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
275pub struct AttributeType(u16);
276
277impl core::fmt::Display for AttributeType {
278    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
279        write!(f, "{}({:#x}: {})", self.0, self.0, self.name())
280    }
281}
282
283impl AttributeType {
284    /// Add the name for a particular [`AttributeType`] for formatting purposes.
285    #[cfg(feature = "std")]
286    pub fn add_name(self, name: &'static str) {
287        let mut anames = ATTRIBUTE_TYPE_NAME_MAP
288            .get_or_init(Default::default)
289            .lock()
290            .unwrap();
291        anames.insert(self, name);
292    }
293
294    /// Create a new AttributeType from an existing value.
295    ///
296    /// Note: the value passed in is not encoded as in a stun message.
297    ///
298    /// # Examples
299    /// ```
300    /// # use stun_types::attribute::AttributeType;
301    /// assert_eq!(AttributeType::new(0x123).value(), 0x123);
302    /// ```
303    pub const fn new(val: u16) -> Self {
304        Self(val)
305    }
306
307    /// Return the integer value of this AttributeType.
308    ///
309    /// Note: the value returned is not encoded as in a stun message.
310    ///
311    /// # Examples
312    /// ```
313    /// # use stun_types::attribute::AttributeType;
314    /// assert_eq!(AttributeType::new(0x123).value(), 0x123);
315    /// ```
316    pub fn value(&self) -> u16 {
317        self.0
318    }
319
320    /// Returns a human readable name of this `AttributeType` or "unknown".
321    ///
322    /// # Examples
323    /// ```
324    /// # use stun_types::attribute::*;
325    /// assert_eq!(XorMappedAddress::TYPE.name(), "XOR-MAPPED-ADDRESS");
326    /// ```
327    pub fn name(self) -> &'static str {
328        match self {
329            AttributeType(0x0001) => "MAPPED-ADDRESS",
330            Username::TYPE => "USERNAME",
331            MessageIntegrity::TYPE => "MESSAGE-INTEGRITY",
332            ErrorCode::TYPE => "ERROR-CODE",
333            UnknownAttributes::TYPE => "UNKNOWN-ATTRIBUTES",
334            Realm::TYPE => "REALM",
335            Nonce::TYPE => "NONCE",
336            MessageIntegritySha256::TYPE => "MESSAGE-INTEGRITY-SHA256",
337            PasswordAlgorithm::TYPE => "PASSWORD-ALGORITHM",
338            Userhash::TYPE => "USERHASH",
339            XorMappedAddress::TYPE => "XOR-MAPPED-ADDRESS",
340            PasswordAlgorithms::TYPE => "PASSWORD_ALGORITHMS",
341            AlternateDomain::TYPE => "ALTERNATE-DOMAIN",
342            Software::TYPE => "SOFTWARE",
343            AlternateServer::TYPE => "ALTERNATE-SERVER",
344            Fingerprint::TYPE => "FINGERPRINT",
345            _ => {
346                #[cfg(feature = "std")]
347                {
348                    let anames = ATTRIBUTE_TYPE_NAME_MAP
349                        .get_or_init(Default::default)
350                        .lock()
351                        .unwrap();
352                    if let Some(name) = anames.get(&self) {
353                        return name;
354                    }
355                }
356                "unknown"
357            }
358        }
359    }
360
361    /// Check if comprehension is required for an `AttributeType`.
362    ///
363    /// All attribute values < 0x8000 require comprehension.
364    ///
365    /// # Examples
366    ///
367    /// ```
368    /// # use stun_types::attribute::AttributeType;
369    /// assert_eq!(AttributeType::new(0x0).comprehension_required(), true);
370    /// assert_eq!(AttributeType::new(0x8000).comprehension_required(), false);
371    /// ```
372    pub fn comprehension_required(self) -> bool {
373        self.0 < 0x8000
374    }
375}
376impl From<u16> for AttributeType {
377    fn from(f: u16) -> Self {
378        Self::new(f)
379    }
380}
381impl From<AttributeType> for u16 {
382    fn from(f: AttributeType) -> Self {
383        f.0
384    }
385}
386
387/// Structure for holding the header of a STUN attribute.
388///
389/// Contains the type and the length.
390#[derive(Debug, Copy, Clone, PartialEq, Eq)]
391pub struct AttributeHeader {
392    atype: AttributeType,
393    length: u16,
394}
395
396impl AttributeHeader {
397    fn parse(data: &[u8]) -> Result<Self, StunParseError> {
398        if data.len() < 4 {
399            return Err(StunParseError::Truncated {
400                expected: 4,
401                actual: data.len(),
402            });
403        }
404        let ret = Self {
405            atype: BigEndian::read_u16(&data[0..2]).into(),
406            length: BigEndian::read_u16(&data[2..4]),
407        };
408        Ok(ret)
409    }
410
411    fn to_bytes(self) -> [u8; 4] {
412        let mut ret = [0; 4];
413        self.write_into(&mut ret);
414        ret
415    }
416
417    fn write_into(&self, ret: &mut [u8]) {
418        BigEndian::write_u16(&mut ret[0..2], self.atype.into());
419        BigEndian::write_u16(&mut ret[2..4], self.length);
420    }
421
422    /// Returns the type of the attribute.
423    pub fn get_type(&self) -> AttributeType {
424        self.atype
425    }
426
427    /// Returns the length in bytes of the attribute.
428    pub fn length(&self) -> u16 {
429        self.length
430    }
431}
432impl From<AttributeHeader> for [u8; 4] {
433    fn from(f: AttributeHeader) -> Self {
434        f.to_bytes()
435    }
436}
437impl TryFrom<&[u8]> for AttributeHeader {
438    type Error = StunParseError;
439
440    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
441        AttributeHeader::parse(value)
442    }
443}
444
445/// A static type for an [`Attribute`].
446pub trait AttributeStaticType {
447    /// The [`AttributeType`].
448    const TYPE: AttributeType;
449}
450
451/// A STUN attribute for use in [`Message`](crate::message::Message)s.
452pub trait Attribute: core::fmt::Debug + core::marker::Sync + core::marker::Send {
453    /// Retrieve the type of an `Attribute`.
454    fn get_type(&self) -> AttributeType;
455
456    /// Retrieve the length of an `Attribute`.  This is not the padded length as stored in a
457    /// `Message` and does not include the size of the attribute header.
458    fn length(&self) -> u16;
459}
460
461/// A trait for converting from a [`RawAttribute`] to a concrete [`Attribute`].
462pub trait AttributeFromRaw<'a>: Attribute {
463    /// Produce an `Attribute` from a `RawAttribute`.
464    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
465    where
466        Self: Sized;
467
468    /// Produce an `Attribute` from a `RawAttribute`.
469    fn from_raw(raw: RawAttribute<'a>) -> Result<Self, StunParseError>
470    where
471        Self: Sized,
472    {
473        Self::from_raw_ref(&raw)
474    }
475}
476
477/// Computes the padded length of an attribute value to a multiple of 4 bytes.
478pub fn pad_attribute_len(len: usize) -> usize {
479    if len % 4 == 0 {
480        len
481    } else {
482        len + 4 - len % 4
483    }
484}
485
486/// Automatically implemented trait providing some helper functions for [`Attribute`]s.
487pub trait AttributeExt {
488    /// The length in bytes of an [`Attribute`] as stored in a [`Message`](crate::message::Message)
489    /// including any padding and the attribute header.
490    fn padded_len(&self) -> usize;
491}
492
493impl<A: Attribute + ?Sized> AttributeExt for A {
494    fn padded_len(&self) -> usize {
495        4 + pad_attribute_len(self.length() as usize)
496    }
497}
498
499/// Trait required when implementing writing an [`Attribute`] to a sequence of bytes
500pub trait AttributeWrite: Attribute {
501    /// Write attribute to the provided destination buffer.
502    ///
503    /// Panics if the destination buffer is not large enough.
504    fn write_into_unchecked(&self, dest: &mut [u8]);
505    /// Produce a [`RawAttribute`] from this [`Attribute`].
506    fn to_raw(&self) -> RawAttribute<'_>;
507}
508
509/// Automatically implemented trait providing helper functionality for writing an [`Attribute`] to
510/// a sequence of bytes.
511pub trait AttributeWriteExt: AttributeWrite {
512    /// Write the 4 byte attribute header into the provided destination buffer returning the
513    /// number of bytes written.
514    ///
515    /// Panics if the destination cannot hold at least 4 bytes of data.
516    fn write_header_unchecked(&self, dest: &mut [u8]) -> usize;
517    /// Write the 4 byte attribute header into the provided destination buffer returning the
518    /// number of bytes written, or an error.
519    fn write_header(&self, dest: &mut [u8]) -> Result<usize, StunWriteError>;
520    /// Write this attribute into the provided destination buffer returning the number of bytes
521    /// written, or an error.
522    fn write_into(&self, dest: &mut [u8]) -> Result<usize, StunWriteError>;
523}
524
525impl<A: AttributeWrite + ?Sized> AttributeWriteExt for A {
526    fn write_header(&self, dest: &mut [u8]) -> Result<usize, StunWriteError> {
527        if dest.len() < 4 {
528            return Err(StunWriteError::TooSmall {
529                expected: 4,
530                actual: dest.len(),
531            });
532        }
533        self.write_header_unchecked(dest);
534        Ok(4)
535    }
536    fn write_header_unchecked(&self, dest: &mut [u8]) -> usize {
537        AttributeHeader {
538            atype: self.get_type(),
539            length: self.length(),
540        }
541        .write_into(dest);
542        4
543    }
544
545    fn write_into(&self, dest: &mut [u8]) -> Result<usize, StunWriteError> {
546        let len = self.padded_len();
547        if len > dest.len() {
548            return Err(StunWriteError::TooSmall {
549                expected: len,
550                actual: dest.len(),
551            });
552        }
553        self.write_into_unchecked(dest);
554        Ok(len)
555    }
556}
557
558/// The header and raw bytes of an unparsed [`Attribute`].
559#[derive(Debug, Clone, PartialEq, Eq)]
560pub struct RawAttribute<'a> {
561    /// The [`AttributeHeader`] of this [`RawAttribute`].
562    pub header: AttributeHeader,
563    /// The raw bytes of this [`RawAttribute`].
564    pub value: Data<'a>,
565}
566
567macro_rules! display_attr {
568    ($this:ident, $f:ident, $CamelType:ty) => {{
569        if let Ok(attr) = <$CamelType>::from_raw_ref($this) {
570            write!($f, "{}", attr)
571        } else {
572            write!(
573                $f,
574                "{}(Malformed): len: {}, data: {:?})",
575                $this.get_type(),
576                $this.header.length(),
577                $this.value
578            )
579        }
580    }};
581}
582
583impl core::fmt::Display for RawAttribute<'_> {
584    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
585        // try to get a more specialised display
586        match self.get_type() {
587            Username::TYPE => display_attr!(self, f, Username),
588            MessageIntegrity::TYPE => display_attr!(self, f, MessageIntegrity),
589            ErrorCode::TYPE => display_attr!(self, f, ErrorCode),
590            UnknownAttributes::TYPE => display_attr!(self, f, UnknownAttributes),
591            Realm::TYPE => display_attr!(self, f, Realm),
592            Nonce::TYPE => display_attr!(self, f, Nonce),
593            MessageIntegritySha256::TYPE => {
594                display_attr!(self, f, MessageIntegritySha256)
595            }
596            PasswordAlgorithm::TYPE => display_attr!(self, f, PasswordAlgorithm),
597            Userhash::TYPE => display_attr!(self, f, Userhash),
598            XorMappedAddress::TYPE => display_attr!(self, f, XorMappedAddress),
599            PasswordAlgorithms::TYPE => display_attr!(self, f, PasswordAlgorithms),
600            AlternateDomain::TYPE => display_attr!(self, f, AlternateDomain),
601            Software::TYPE => display_attr!(self, f, Software),
602            AlternateServer::TYPE => display_attr!(self, f, AlternateServer),
603            Fingerprint::TYPE => display_attr!(self, f, Fingerprint),
604            _ => {
605                #[cfg(feature = "std")]
606                {
607                    let mut display_impls = ATTRIBUTE_EXTERNAL_DISPLAY_IMPL
608                        .get_or_init(|| Default::default())
609                        .lock()
610                        .unwrap();
611                    if let Some(imp) = display_impls.get_mut(&self.get_type()) {
612                        return imp(self, f);
613                    }
614                }
615                write!(
616                    f,
617                    "RawAttribute (type: {:?}, len: {}, data: {:?})",
618                    self.header.get_type(),
619                    self.header.length(),
620                    &self.value
621                )
622            }
623        }
624    }
625}
626
627impl<'a> RawAttribute<'a> {
628    /// Create a new [`RawAttribute`].
629    pub fn new(atype: AttributeType, data: &'a [u8]) -> Self {
630        Self {
631            header: AttributeHeader {
632                atype,
633                length: data.len() as u16,
634            },
635            value: data.into(),
636        }
637    }
638
639    /// Create a new owned [`RawAttribute`].
640    pub fn new_owned(atype: AttributeType, data: Box<[u8]>) -> Self {
641        Self {
642            header: AttributeHeader {
643                atype,
644                length: data.len() as u16,
645            },
646            value: data.into(),
647        }
648    }
649
650    /// Deserialize a `RawAttribute` from bytes.
651    ///
652    /// # Examples
653    ///
654    /// ```
655    /// # use stun_types::attribute::{RawAttribute, Attribute, AttributeType};
656    /// let data = &[0, 1, 0, 2, 5, 6, 0, 0];
657    /// let attr = RawAttribute::from_bytes(data).unwrap();
658    /// assert_eq!(attr.get_type(), AttributeType::new(1));
659    /// assert_eq!(attr.length(), 2);
660    /// ```
661    pub fn from_bytes(data: &'a [u8]) -> Result<Self, StunParseError> {
662        let header = AttributeHeader::parse(data)?;
663        // the advertised length is larger than actual data -> error
664        if header.length() > (data.len() - 4) as u16 {
665            return Err(StunParseError::Truncated {
666                expected: header.length() as usize,
667                actual: data.len() - 4,
668            });
669        }
670        Ok(Self {
671            header,
672            value: Data::Borrowed(data[4..header.length() as usize + 4].into()),
673        })
674    }
675
676    /// Serialize a `RawAttribute` to bytes.
677    ///
678    /// # Examples
679    ///
680    /// ```
681    /// # use stun_types::attribute::{RawAttribute, Attribute, AttributeType};
682    /// let attr = RawAttribute::new(AttributeType::new(1), &[5, 6]);
683    /// assert_eq!(attr.to_bytes(), &[0, 1, 0, 2, 5, 6, 0, 0]);
684    /// ```
685    pub fn to_bytes(&self) -> Vec<u8> {
686        let mut vec = Vec::with_capacity(self.padded_len());
687        let mut header_bytes = [0; 4];
688        self.header.write_into(&mut header_bytes);
689        vec.extend(&header_bytes);
690        vec.extend(&*self.value);
691        let len = vec.len();
692        if len % 4 != 0 {
693            // pad to 4 bytes
694            vec.resize(len + 4 - (len % 4), 0);
695        }
696        vec
697    }
698
699    /// Helper for checking that a raw attribute is of a particular type and has a data length
700    /// within a certain range.
701    pub fn check_type_and_len(
702        &self,
703        atype: AttributeType,
704        allowed_range: impl core::ops::RangeBounds<usize>,
705    ) -> Result<(), StunParseError> {
706        if self.header.get_type() != atype {
707            return Err(StunParseError::WrongAttributeImplementation);
708        }
709        check_len(self.value.len(), allowed_range)
710    }
711
712    /// Consume this [`RawAttribute`] and return a new owned [`RawAttribute`].
713    pub fn into_owned<'b>(self) -> RawAttribute<'b> {
714        RawAttribute {
715            header: self.header,
716            value: self.value.into_owned(),
717        }
718    }
719}
720
721impl Attribute for RawAttribute<'_> {
722    /// Returns the [`AttributeType`] of this [`RawAttribute`].
723    fn get_type(&self) -> AttributeType {
724        self.header.get_type()
725    }
726
727    /// Returns the length in bytes of this [`RawAttribute`].
728    fn length(&self) -> u16 {
729        self.value.len() as u16
730    }
731}
732
733impl AttributeWrite for RawAttribute<'_> {
734    /// Write this [`RawAttribute`] into a byte slice.
735    ///
736    /// Returns the number of bytes written.
737    fn write_into_unchecked(&self, dest: &mut [u8]) {
738        let len = self.padded_len();
739        self.header.write_into(dest);
740        let mut offset = 4;
741        dest[offset..offset + self.value.len()].copy_from_slice(&self.value);
742        offset += self.value.len();
743        if len - offset > 0 {
744            dest[offset..len].fill(0);
745        }
746    }
747
748    fn to_raw(&self) -> RawAttribute<'_> {
749        self.clone()
750    }
751}
752
753impl<'a, A: AttributeWrite> From<&'a A> for RawAttribute<'a> {
754    fn from(value: &'a A) -> Self {
755        value.to_raw()
756    }
757}
758
759fn check_len(
760    len: usize,
761    allowed_range: impl core::ops::RangeBounds<usize>,
762) -> Result<(), StunParseError> {
763    match allowed_range.start_bound() {
764        core::ops::Bound::Unbounded => (),
765        core::ops::Bound::Included(start) => {
766            if len < *start {
767                return Err(StunParseError::Truncated {
768                    expected: *start,
769                    actual: len,
770                });
771            }
772        }
773        core::ops::Bound::Excluded(start) => {
774            if len <= *start {
775                return Err(StunParseError::Truncated {
776                    expected: start + 1,
777                    actual: len,
778                });
779            }
780        }
781    }
782    match allowed_range.end_bound() {
783        core::ops::Bound::Unbounded => (),
784        core::ops::Bound::Included(end) => {
785            if len > *end {
786                return Err(StunParseError::TooLarge {
787                    expected: *end,
788                    actual: len,
789                });
790            }
791        }
792        core::ops::Bound::Excluded(end) => {
793            if len >= *end {
794                return Err(StunParseError::TooLarge {
795                    expected: *end - 1,
796                    actual: len,
797                });
798            }
799        }
800    }
801    Ok(())
802}
803
804impl From<RawAttribute<'_>> for Vec<u8> {
805    fn from(f: RawAttribute) -> Self {
806        f.to_bytes()
807    }
808}
809
810#[cfg(test)]
811mod tests {
812    use super::*;
813
814    #[test]
815    fn attribute_type() {
816        let _log = crate::tests::test_init_log();
817        let atype = ErrorCode::TYPE;
818        let anum: u16 = atype.into();
819        assert_eq!(atype, anum.into());
820    }
821
822    #[test]
823    fn short_attribute_header() {
824        let _log = crate::tests::test_init_log();
825        let data = [0; 1];
826        // not enough data to parse the header
827        let res: Result<AttributeHeader, _> = data.as_ref().try_into();
828        assert!(res.is_err());
829    }
830
831    #[test]
832    fn raw_attribute_construct() {
833        let _log = crate::tests::test_init_log();
834        let a = RawAttribute::new(1.into(), &[80, 160]);
835        assert_eq!(a.get_type(), 1.into());
836        let bytes: Vec<_> = a.into();
837        assert_eq!(bytes, &[0, 1, 0, 2, 80, 160, 0, 0]);
838        let b = RawAttribute::from_bytes(bytes.as_ref()).unwrap();
839        assert_eq!(b.get_type(), 1.into());
840    }
841
842    #[test]
843    fn raw_attribute_encoding() {
844        let mut out = [0; 8];
845        let mut out2 = [0; 8];
846        let _log = crate::tests::test_init_log();
847        let orig = RawAttribute::new(1.into(), &[80, 160]);
848        assert_eq!(orig.get_type(), 1.into());
849        orig.write_into(&mut out).unwrap();
850        orig.write_into_unchecked(&mut out2);
851        assert_eq!(out, out2);
852        let mut data: Vec<_> = orig.into();
853        let len = data.len();
854        // one byte too big vs data size
855        BigEndian::write_u16(&mut data[2..4], len as u16 - 4 + 1);
856        assert!(matches!(
857            RawAttribute::from_bytes(data.as_ref()),
858            Err(StunParseError::Truncated {
859                expected: 5,
860                actual: 4
861            })
862        ));
863    }
864
865    #[test]
866    fn raw_attribute_header() {
867        let mut out = [0; 4];
868        let mut out2 = [0; 4];
869        let _log = crate::tests::test_init_log();
870        let orig = RawAttribute::new(1.into(), &[80, 160]);
871        assert!(matches!(orig.write_header(&mut out), Ok(4)));
872        assert_eq!(orig.write_header_unchecked(&mut out2), 4);
873        assert_eq!(out, out2);
874        assert_eq!(orig.header.to_bytes(), out);
875        assert_eq!(&orig.to_bytes()[..4], out);
876        let bytes: [_; 4] = orig.header.into();
877        assert_eq!(bytes, out);
878    }
879
880    #[test]
881    fn raw_attribute_write_into_small() {
882        let mut out = [0; 8];
883        let _log = crate::tests::test_init_log();
884        let orig = RawAttribute::new(1.into(), &[80, 160]);
885        assert_eq!(orig.get_type(), 1.into());
886        assert!(matches!(
887            orig.write_header(&mut out[..3]),
888            Err(StunWriteError::TooSmall {
889                expected: 4,
890                actual: 3,
891            })
892        ));
893        assert!(matches!(
894            orig.write_into(&mut out[..7]),
895            Err(StunWriteError::TooSmall {
896                expected: 8,
897                actual: 7,
898            })
899        ));
900    }
901
902    #[test]
903    fn test_check_len() {
904        let _log = crate::tests::test_init_log();
905        assert!(check_len(4, ..).is_ok());
906        assert!(check_len(4, 0..).is_ok());
907        assert!(check_len(4, 0..8).is_ok());
908        assert!(check_len(4, 0..=8).is_ok());
909        assert!(check_len(4, ..=8).is_ok());
910        assert!(matches!(
911            check_len(4, ..4),
912            Err(StunParseError::TooLarge {
913                expected: 3,
914                actual: 4
915            })
916        ));
917        assert!(matches!(
918            check_len(4, 5..),
919            Err(StunParseError::Truncated {
920                expected: 5,
921                actual: 4
922            })
923        ));
924        assert!(matches!(
925            check_len(4, ..=3),
926            Err(StunParseError::TooLarge {
927                expected: 3,
928                actual: 4
929            })
930        ));
931        assert!(matches!(
932            check_len(
933                4,
934                (core::ops::Bound::Excluded(4), core::ops::Bound::Unbounded)
935            ),
936            Err(StunParseError::Truncated {
937                expected: 5,
938                actual: 4
939            })
940        ));
941    }
942
943    #[test]
944    #[cfg(feature = "std")]
945    fn test_external_display_impl() {
946        use crate::message::TransactionId;
947
948        let _log = crate::tests::test_init_log();
949        let atype = AttributeType::new(0xFFFF);
950        assert_eq!(atype.name(), "unknown");
951        let data = [4, 0];
952        let attr = RawAttribute::new(atype, &data);
953        assert_eq!(
954            alloc::format!("{attr}"),
955            "RawAttribute (type: AttributeType(65535), len: 2, data: Borrowed(DataSlice([4, 0])))"
956        );
957        let imp = |attr: &RawAttribute<'_>,
958                   f: &mut core::fmt::Formatter<'_>|
959         -> core::fmt::Result { write!(f, "Custom {}", attr.value[0]) };
960        add_display_impl(atype, imp);
961        let display_str = alloc::format!("{}", attr);
962        assert_eq!(display_str, "Custom 4");
963
964        atype.add_name("SOME-NAME");
965        assert_eq!(atype.name(), "SOME-NAME");
966
967        attribute_display!(Fingerprint);
968
969        let id = TransactionId::generate();
970        let xor_addr = XorMappedAddress::new("127.0.0.1:10000".parse().unwrap(), id);
971        let raw = xor_addr.to_raw();
972        let raw = RawAttribute::new(raw.get_type(), &raw.value[..3]);
973        assert_eq!(alloc::format!("{raw}"), "32(0x20: XOR-MAPPED-ADDRESS)(Malformed): len: 3, data: Borrowed(DataSlice([0, 1, 6])))");
974    }
975}