Skip to main content

turn_types/attribute/
transport.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 [`RequestedTransport`] [`Attribute`].
14///
15/// Request a particular transport for an TURN allocation.
16///
17/// Reference: [RFC5766 Section 14.7](https://datatracker.ietf.org/doc/html/rfc5766#section-14.7).
18#[derive(Debug, Clone)]
19pub struct RequestedTransport {
20    protocol: u8,
21}
22
23impl AttributeStaticType for RequestedTransport {
24    const TYPE: AttributeType = AttributeType::new(0x0019);
25}
26
27impl Attribute for RequestedTransport {
28    fn get_type(&self) -> AttributeType {
29        Self::TYPE
30    }
31
32    fn length(&self) -> u16 {
33        4
34    }
35}
36
37impl AttributeWrite for RequestedTransport {
38    fn to_raw(&self) -> RawAttribute<'_> {
39        RawAttribute::new(self.get_type(), &[self.protocol, 0, 0, 0]).into_owned()
40    }
41
42    fn write_into_unchecked(&self, dest: &mut [u8]) {
43        self.write_header_unchecked(dest);
44        dest[4] = self.protocol;
45        dest[5] = 0x0;
46        dest[6] = 0x0;
47        dest[7] = 0x0;
48    }
49}
50
51impl AttributeFromRaw<'_> for RequestedTransport {
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 RequestedTransport {
61    type Error = StunParseError;
62    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
63        raw.check_type_and_len(Self::TYPE, 4..=4)?;
64        Ok(Self {
65            protocol: raw.value[0],
66        })
67    }
68}
69
70impl RequestedTransport {
71    /// The TCP transport type.
72    pub const TCP: u8 = 6;
73    /// The UDP transport type.
74    pub const UDP: u8 = 17;
75
76    /// Create a new [`RequestedTransport`] [`Attribute`].
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// # use turn_types::attribute::*;
82    /// let requested_transport = RequestedTransport::new(RequestedTransport::UDP);
83    /// assert_eq!(requested_transport.protocol(), RequestedTransport::UDP);
84    /// ```
85    pub fn new(protocol: u8) -> Self {
86        Self { protocol }
87    }
88
89    /// Retrieve the protocol stored in a [`RequestedTransport`]
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// # use turn_types::attribute::*;
95    /// let requested_transport = RequestedTransport::new(RequestedTransport::TCP);
96    /// assert_eq!(requested_transport.protocol(), RequestedTransport::TCP);
97    /// ```
98    pub fn protocol(&self) -> u8 {
99        self.protocol
100    }
101}
102
103impl core::fmt::Display for RequestedTransport {
104    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
105        write!(f, "{}: {}", self.get_type(), self.protocol())
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use alloc::vec::Vec;
113    use byteorder::{BigEndian, ByteOrder};
114    use std::println;
115
116    #[test]
117    fn requested_transport() {
118        let _log = crate::tests::test_init_log();
119        let trans = RequestedTransport::new(17);
120        assert_eq!(trans.get_type(), RequestedTransport::TYPE);
121        assert_eq!(trans.protocol(), 17);
122        let raw: RawAttribute = trans.to_raw();
123        println!("raw: {raw:?}");
124        assert_eq!(raw.get_type(), RequestedTransport::TYPE);
125        let trans2 = RequestedTransport::try_from(&raw).unwrap();
126        assert_eq!(trans2.get_type(), RequestedTransport::TYPE);
127        assert_eq!(trans2.protocol(), 17);
128        // provide incorrectly typed data
129        let mut data: Vec<_> = raw.into();
130        BigEndian::write_u16(&mut data[0..2], 0);
131        assert!(matches!(
132            RequestedTransport::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
133            Err(StunParseError::WrongAttributeImplementation)
134        ));
135        let mut data = [0; 8];
136        trans.write_into(&mut data).unwrap();
137        let raw = RawAttribute::from_bytes(&data).unwrap();
138        let trans2 = RequestedTransport::from_raw_ref(&raw).unwrap();
139        assert_eq!(trans.protocol(), trans2.protocol());
140    }
141}