Skip to main content

turn_types/attribute/
channel.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 byteorder::{BigEndian, ByteOrder};
12
13use stun_types::{attribute::*, message::StunParseError};
14
15/// The [`ChannelNumber`] [`Attribute`].
16///
17/// Typically used when setting a TURN channel using the
18/// [`CHANNEL_BIND`](crate::message::CHANNEL_BIND) method.
19///
20/// Reference: [RFC5766 section 14.1](https://datatracker.ietf.org/doc/html/rfc5766#section-14.1).
21#[derive(Debug, Clone)]
22pub struct ChannelNumber {
23    channel: u16,
24}
25
26impl AttributeStaticType for ChannelNumber {
27    const TYPE: AttributeType = AttributeType::new(0x000C);
28}
29
30impl Attribute for ChannelNumber {
31    fn get_type(&self) -> AttributeType {
32        Self::TYPE
33    }
34
35    fn length(&self) -> u16 {
36        4
37    }
38}
39
40impl AttributeWrite for ChannelNumber {
41    fn to_raw(&self) -> RawAttribute<'_> {
42        let mut buf = [0; 4];
43        BigEndian::write_u16(&mut buf[..2], self.channel);
44        RawAttribute::new(self.get_type(), &buf).into_owned()
45    }
46    fn write_into_unchecked(&self, dest: &mut [u8]) {
47        self.write_header_unchecked(dest);
48        BigEndian::write_u16(&mut dest[4..6], self.channel);
49        dest[7..self.padded_len()].fill(0);
50    }
51}
52
53impl AttributeFromRaw<'_> for ChannelNumber {
54    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
55    where
56        Self: Sized,
57    {
58        Self::try_from(raw)
59    }
60}
61
62impl TryFrom<&RawAttribute<'_>> for ChannelNumber {
63    type Error = StunParseError;
64    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
65        raw.check_type_and_len(Self::TYPE, 4..=4)?;
66        Ok(Self {
67            channel: BigEndian::read_u16(&raw.value),
68        })
69    }
70}
71
72impl ChannelNumber {
73    /// Create a new [`ChannelNumber`] [`Attribute`].
74    ///
75    /// # Examples
76    ///
77    /// ```
78    /// # use turn_types::attribute::*;
79    /// let channel = ChannelNumber::new (42);
80    /// assert_eq!(channel.channel(), 42);
81    /// ```
82    pub fn new(channel: u16) -> Self {
83        Self { channel }
84    }
85
86    /// The channel number stored in a [`ChannelNumber`] [`Attribute`].
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// # use turn_types::attribute::*;
92    /// let channel = ChannelNumber::new (42);
93    /// assert_eq!(channel.channel(), 42);
94    /// ```
95    pub fn channel(&self) -> u16 {
96        self.channel
97    }
98}
99
100impl core::fmt::Display for ChannelNumber {
101    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102        write!(f, "{}: '{}'", self.get_type(), self.channel)
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use alloc::{vec, vec::Vec};
110    use byteorder::{BigEndian, ByteOrder};
111    use tracing::trace;
112
113    #[test]
114    fn channel_number() {
115        let _log = crate::tests::test_init_log();
116        let c = ChannelNumber::new(6);
117        assert_eq!(c.get_type(), ChannelNumber::TYPE);
118        assert_eq!(c.channel(), 6);
119    }
120
121    #[test]
122    fn channel_number_raw() {
123        let _log = crate::tests::test_init_log();
124        let c = ChannelNumber::new(6);
125        assert_eq!(c.get_type(), ChannelNumber::TYPE);
126        assert_eq!(c.channel(), 6);
127        let raw: RawAttribute = c.to_raw();
128        trace!("{}", raw);
129        assert_eq!(raw.get_type(), ChannelNumber::TYPE);
130        let c2 = ChannelNumber::try_from(&raw).unwrap();
131        assert_eq!(c2.get_type(), ChannelNumber::TYPE);
132        assert_eq!(c2.channel(), 6);
133    }
134
135    #[test]
136    fn channel_number_raw_wrong_type() {
137        let _log = crate::tests::test_init_log();
138        let c = ChannelNumber::new(6);
139        assert_eq!(c.get_type(), ChannelNumber::TYPE);
140        assert_eq!(c.channel(), 6);
141        let raw: RawAttribute = c.to_raw();
142        // provide incorrectly typed data
143        let mut data: Vec<_> = raw.into();
144        BigEndian::write_u16(&mut data[0..2], 0);
145        assert!(matches!(
146            ChannelNumber::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
147            Err(StunParseError::WrongAttributeImplementation)
148        ));
149    }
150
151    #[test]
152    fn channel_number_write_into() {
153        let _log = crate::tests::test_init_log();
154        let c = ChannelNumber::new(6);
155        assert_eq!(c.channel(), 6);
156        let raw: RawAttribute = c.to_raw();
157
158        let mut dest = vec![0; raw.padded_len()];
159        c.write_into(&mut dest).unwrap();
160        let raw = RawAttribute::from_bytes(&dest).unwrap();
161        let c2 = ChannelNumber::try_from(&raw).unwrap();
162        assert_eq!(c2.get_type(), ChannelNumber::TYPE);
163        assert_eq!(c2.channel(), c.channel());
164    }
165
166    #[test]
167    #[should_panic = "out of range"]
168    fn channel_number_write_into_unchecked() {
169        let _log = crate::tests::test_init_log();
170        let c = ChannelNumber::new(6);
171        assert_eq!(c.channel(), 6);
172        let raw: RawAttribute = c.to_raw();
173
174        let mut dest = vec![0; raw.padded_len() - 1];
175        c.write_into_unchecked(&mut dest);
176    }
177}