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 UDP transport type.
70    pub const UDP: u8 = 17;
71
72    /// Create a new [`RequestedTransport`] [`Attribute`].
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// # use turn_types::attribute::*;
78    /// let requested_transport = RequestedTransport::new(RequestedTransport::UDP);
79    /// assert_eq!(requested_transport.protocol(), RequestedTransport::UDP);
80    /// ```
81    pub fn new(protocol: u8) -> Self {
82        Self { protocol }
83    }
84
85    /// Retrieve the protocol stored in a [`RequestedTransport`]
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// # use turn_types::attribute::*;
91    /// let requested_transport = RequestedTransport::new(RequestedTransport::UDP);
92    /// assert_eq!(requested_transport.protocol(), RequestedTransport::UDP);
93    /// ```
94    pub fn protocol(&self) -> u8 {
95        self.protocol
96    }
97}
98
99impl std::fmt::Display for RequestedTransport {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        write!(f, "{}: {}", self.get_type(), self.protocol())
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use byteorder::{BigEndian, ByteOrder};
109
110    #[test]
111    fn requested_transport() {
112        let _log = crate::tests::test_init_log();
113        let trans = RequestedTransport::new(17);
114        assert_eq!(trans.get_type(), RequestedTransport::TYPE);
115        assert_eq!(trans.protocol(), 17);
116        let raw: RawAttribute = trans.to_raw();
117        println!("raw: {raw:?}");
118        assert_eq!(raw.get_type(), RequestedTransport::TYPE);
119        let trans2 = RequestedTransport::try_from(&raw).unwrap();
120        assert_eq!(trans2.get_type(), RequestedTransport::TYPE);
121        assert_eq!(trans2.protocol(), 17);
122        // provide incorrectly typed data
123        let mut data: Vec<_> = raw.into();
124        BigEndian::write_u16(&mut data[0..2], 0);
125        assert!(matches!(
126            RequestedTransport::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
127            Err(StunParseError::WrongAttributeImplementation)
128        ));
129        let mut data = [0; 8];
130        trans.write_into(&mut data).unwrap();
131        let raw = RawAttribute::from_bytes(&data).unwrap();
132        let trans2 = RequestedTransport::from_raw_ref(&raw).unwrap();
133        assert_eq!(trans.protocol(), trans2.protocol());
134    }
135}