Skip to main content

turn_types/attribute/
data.rs

1// Copyright (C) 2025 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 stun_types::{attribute::*, message::StunParseError};
12
13/// The [`Data`] [`Attribute`].
14///
15/// Holds a sequence of bytes to transfer.
16///
17/// Reference: [RFC5766 Section 14.4](https://datatracker.ietf.org/doc/html/rfc5766#section-14.4).
18#[derive(Debug, Clone)]
19pub struct Data<'a> {
20    data: stun_types::data::Data<'a>,
21}
22
23impl AttributeStaticType for Data<'_> {
24    const TYPE: AttributeType = AttributeType::new(0x0013);
25}
26
27impl Attribute for Data<'_> {
28    fn get_type(&self) -> AttributeType {
29        Self::TYPE
30    }
31
32    fn length(&self) -> u16 {
33        self.data.len() as u16
34    }
35}
36
37impl AttributeWrite for Data<'_> {
38    fn to_raw(&self) -> RawAttribute<'_> {
39        RawAttribute::new(self.get_type(), &self.data)
40    }
41
42    fn write_into_unchecked(&self, dest: &mut [u8]) {
43        self.write_header_unchecked(dest);
44        dest[4..4 + self.data.len()].copy_from_slice(&self.data);
45        dest[4 + self.data.len()..self.padded_len()].fill(0);
46    }
47}
48
49impl<'a> AttributeFromRaw<'a> for Data<'a> {
50    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
51    where
52        Self: Sized,
53    {
54        raw.check_type_and_len(Self::TYPE, ..)?;
55        Ok(Self {
56            data: raw.value.clone().into_owned(),
57        })
58    }
59
60    fn from_raw(raw: RawAttribute<'a>) -> Result<Self, StunParseError>
61    where
62        Self: Sized,
63    {
64        Self::try_from(raw)
65    }
66}
67
68impl<'a> TryFrom<RawAttribute<'a>> for Data<'a> {
69    type Error = StunParseError;
70
71    fn try_from(raw: RawAttribute<'a>) -> Result<Self, Self::Error> {
72        raw.check_type_and_len(Self::TYPE, ..)?;
73        Ok(Self { data: raw.value })
74    }
75}
76
77impl<'a> Data<'a> {
78    /// Create a new Data [`Attribute`].
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// # use turn_types::attribute::*;
84    /// let bytes = vec![0, 1, 2];
85    /// let data = Data::new(&bytes);
86    /// assert_eq!(data.data(), &bytes);
87    /// ```
88    pub fn new(data: &'a [u8]) -> Self {
89        if data.len() > u16::MAX as usize {
90            panic!(
91                "Attempt made to create a Data attribute larger than {}",
92                u16::MAX
93            );
94        }
95        Self {
96            data: stun_types::data::Data::from(data),
97        }
98    }
99
100    /// Retrieve the data stored in a [`Data`].
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// # use turn_types::attribute::*;
106    /// let bytes = vec![0, 1, 2];
107    /// let data = Data::new(&bytes);
108    /// assert_eq!(data.data(), &bytes);
109    /// ```
110    pub fn data(&self) -> &[u8] {
111        &self.data
112    }
113}
114
115impl core::fmt::Display for Data<'_> {
116    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
117        write!(f, "{}: len:{}", self.get_type(), self.data.len())
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use alloc::vec;
125    use alloc::vec::Vec;
126    use byteorder::{BigEndian, ByteOrder};
127    use tracing::trace;
128
129    #[test]
130    fn data() {
131        let _log = crate::tests::test_init_log();
132        let bytes = vec![0, 1, 2, 3, 4, 5];
133        let data = Data::new(&bytes);
134        assert_eq!(data.get_type(), Data::TYPE);
135        assert_eq!(data.data(), &bytes);
136    }
137
138    #[test]
139    fn data_raw() {
140        let _log = crate::tests::test_init_log();
141        let bytes = vec![0, 1, 2, 3, 4, 5];
142        let data = Data::new(&bytes);
143        let raw: RawAttribute = data.to_raw();
144        trace!("{}", raw);
145        assert_eq!(raw.get_type(), Data::TYPE);
146        let data2 = Data::try_from(raw.clone()).unwrap();
147        assert_eq!(data2.get_type(), Data::TYPE);
148        assert_eq!(data2.data(), &bytes);
149    }
150
151    #[test]
152    fn data_raw_wrong_type() {
153        let _log = crate::tests::test_init_log();
154        let bytes = vec![0, 1, 2, 3, 4, 5];
155        let data = Data::new(&bytes);
156        let raw: RawAttribute = data.to_raw();
157        // provide incorrectly typed data
158        let mut data: Vec<_> = raw.into();
159        BigEndian::write_u16(&mut data[0..2], 0);
160        assert!(matches!(
161            Data::try_from(RawAttribute::from_bytes(data.as_ref()).unwrap()),
162            Err(StunParseError::WrongAttributeImplementation)
163        ));
164    }
165
166    #[test]
167    fn data_write_into() {
168        let _log = crate::tests::test_init_log();
169        let bytes = vec![0, 1, 2, 3];
170        let data = Data::new(&bytes);
171        let raw: RawAttribute = data.to_raw();
172        let mut dest = vec![0; raw.padded_len()];
173        data.write_into(&mut dest).unwrap();
174        let raw = RawAttribute::from_bytes(&dest).unwrap();
175        let data2 = Data::try_from(raw.clone()).unwrap();
176        assert_eq!(data2.get_type(), Data::TYPE);
177        assert_eq!(data2.data(), &bytes);
178    }
179
180    #[test]
181    #[should_panic = "out of range"]
182    fn data_write_into_unchecked() {
183        let _log = crate::tests::test_init_log();
184        let bytes = vec![0, 1, 2, 3, 4, 5];
185        let data = Data::new(&bytes);
186        let raw: RawAttribute = data.to_raw();
187        let mut dest = vec![0; raw.padded_len() - 1];
188        data.write_into_unchecked(&mut dest);
189    }
190}