turn_types/attribute/
reservation.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
9use byteorder::{BigEndian, ByteOrder};
10
11use stun_types::{attribute::*, message::StunParseError};
12
13/// The [`ReservationToken`] [`Attribute`].
14///
15/// Reserves an allocation on the TURN server or acquires an already existing allocation on the
16/// TURN server.
17///
18/// Reference: [RFC5766 Section 14.9](https://datatracker.ietf.org/doc/html/rfc5766#section-14.9).
19#[derive(Debug, Clone)]
20pub struct ReservationToken {
21    token: u64,
22}
23
24impl AttributeStaticType for ReservationToken {
25    const TYPE: AttributeType = AttributeType::new(0x0022);
26}
27
28impl Attribute for ReservationToken {
29    fn get_type(&self) -> AttributeType {
30        Self::TYPE
31    }
32
33    fn length(&self) -> u16 {
34        8
35    }
36}
37
38impl AttributeWrite for ReservationToken {
39    fn to_raw(&self) -> RawAttribute<'_> {
40        let mut data = [0; 8];
41        BigEndian::write_u64(&mut data, self.token);
42        RawAttribute::new(self.get_type(), &data).into_owned()
43    }
44
45    fn write_into_unchecked(&self, dest: &mut [u8]) {
46        self.write_header_unchecked(dest);
47        BigEndian::write_u64(&mut dest[4..12], self.token);
48    }
49}
50
51impl AttributeFromRaw<'_> for ReservationToken {
52    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
53    where
54        Self: Sized,
55    {
56        Self::try_from(raw)
57    }
58}
59
60impl TryFrom<&RawAttribute<'_>> for ReservationToken {
61    type Error = StunParseError;
62    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
63        raw.check_type_and_len(Self::TYPE, 8..=8)?;
64        Ok(Self {
65            token: BigEndian::read_u64(&raw.value),
66        })
67    }
68}
69
70impl ReservationToken {
71    /// Create a new [`ReservationToken`] [`Attribute`].
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// # use turn_types::attribute::*;
77    /// let token = ReservationToken::new(100);
78    /// assert_eq!(token.token(), 100);
79    /// ```
80    pub fn new(token: u64) -> Self {
81        Self { token }
82    }
83
84    /// Retrieve the token stored in a [`ReservationToken`].
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// # use turn_types::attribute::*;
90    /// let token = ReservationToken::new(100);
91    /// assert_eq!(token.token(), 100);
92    /// ```
93    pub fn token(&self) -> u64 {
94        self.token
95    }
96}
97
98impl core::fmt::Display for ReservationToken {
99    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100        write!(f, "{}: 0x{:#x}", self.get_type(), self.token())
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use alloc::vec::Vec;
108    use byteorder::{BigEndian, ByteOrder};
109    use std::println;
110
111    #[test]
112    fn reservation_token() {
113        let _log = crate::tests::test_init_log();
114        let token = ReservationToken::new(200);
115        assert_eq!(token.get_type(), ReservationToken::TYPE);
116        assert_eq!(token.token(), 200);
117        let raw: RawAttribute = token.to_raw();
118        println!("{}", raw);
119        assert_eq!(raw.get_type(), ReservationToken::TYPE);
120        let token2 = ReservationToken::try_from(&raw).unwrap();
121        assert_eq!(token2.get_type(), ReservationToken::TYPE);
122        assert_eq!(token2.token(), 200);
123        // provide incorrectly typed data
124        let mut data: Vec<_> = raw.into();
125        BigEndian::write_u16(&mut data[0..2], 0);
126        assert!(matches!(
127            ReservationToken::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
128            Err(StunParseError::WrongAttributeImplementation)
129        ));
130    }
131}