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