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