Skip to main content

turn_types/
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
11//! TURN [`ChannelData`] messages.
12//!
13//! [`ChannelData`] is used as an optional more efficient data transfer mechanism between a TURN
14//! server and a TURN client. A [`ChannelData`] message contains a simple 4-byte header that
15//! contains the channel identifier and the length of the data.
16
17use stun_types::message::StunParseError;
18
19/// A [`ChannelData`] message.
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
21pub struct ChannelData<'a> {
22    id: u16,
23    data: &'a [u8],
24}
25
26impl<'a> ChannelData<'a> {
27    /// Construct a new [`ChannelData`] with the provided identifer and byte sequence.
28    pub fn new(id: u16, data: &'a [u8]) -> Self {
29        Self { id, data }
30    }
31
32    /// The channel identifier stored in this piece of data.
33    pub fn id(&self) -> u16 {
34        self.id
35    }
36
37    /// The sequence of bytes in this message.
38    pub fn data(&self) -> &[u8] {
39        self.data
40    }
41
42    /// Parse a sequence of bytes into a [`ChannelData`].  Returns appropriate errors on failure.
43    ///
44    /// # Examples
45    /// ```
46    /// # use turn_types::channel::*;
47    /// let data = [4; 3];
48    /// let channel = ChannelData::new(0x4000, &data);
49    /// let mut output = [0; 7];
50    /// assert_eq!(7, channel.write_into_unchecked(&mut output));
51    /// let parsed = ChannelData::parse(&output).unwrap();
52    /// assert_eq!(parsed.id(), channel.id());
53    /// assert_eq!(parsed.data(), channel.data());
54    /// ```
55    pub fn parse(data: &'a [u8]) -> Result<Self, StunParseError> {
56        let (id, len) = Self::parse_header(data)?;
57
58        if len + 4 > data.len() {
59            return Err(stun_types::message::StunParseError::Truncated {
60                expected: 4 + len,
61                actual: data.len(),
62            });
63        }
64
65        Ok(ChannelData {
66            id,
67            data: &data[4..4 + len],
68        })
69    }
70
71    /// Parse the header of an [`ChannelData`] returning the channel ID and the length of the
72    /// contained data (without the 4 byte header).
73    ///
74    /// # Examples
75    /// ```
76    /// # use turn_types::channel::*;
77    /// let data = [4; 3];
78    /// let channel = ChannelData::new(0x4000, &data);
79    /// let mut output = [0; 7];
80    /// assert_eq!(7, channel.write_into_unchecked(&mut output));
81    /// let (id, len) = ChannelData::parse_header(&output).unwrap();
82    /// assert_eq!(id, channel.id());
83    /// assert_eq!(len, 3);
84    /// ```
85    pub fn parse_header(data: &[u8]) -> Result<(u16, usize), StunParseError> {
86        if data.len() < 4 {
87            return Err(stun_types::message::StunParseError::Truncated {
88                expected: 4,
89                actual: data.len(),
90            });
91        }
92        let id = u16::from_be_bytes([data[0], data[1]]);
93        let len = u16::from_be_bytes([data[2], data[3]]) as usize;
94
95        if !(0x4000..=0xFFFE).contains(&id) {
96            return Err(stun_types::message::StunParseError::InvalidAttributeData);
97        }
98
99        Ok((id, len))
100    }
101
102    /// Write this [`ChannelData`] into the provided destination slice.
103    ///
104    /// The destination slice must have size `ChannelData::data().len() + 4`.
105    pub fn write_into_unchecked(self, dest: &mut [u8]) -> usize {
106        dest[..2].copy_from_slice(self.id.to_be_bytes().as_ref());
107        dest[2..4].copy_from_slice((self.data.len() as u16).to_be_bytes().as_ref());
108        dest[4..].copy_from_slice(self.data);
109        self.data.len() + 4
110    }
111}
112
113impl core::fmt::Display for ChannelData<'_> {
114    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115        write!(
116            f,
117            "ChannelData(id: {}, data of {} bytes)",
118            self.id,
119            self.data.len()
120        )
121    }
122}
123
124impl AsRef<[u8]> for ChannelData<'_> {
125    fn as_ref(&self) -> &[u8] {
126        self.data
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn channel_data_parse_invalid_id() {
136        let data = [0x00, 0x00, 0x00, 0x00];
137        assert!(matches!(
138            ChannelData::parse(&data),
139            Err(StunParseError::InvalidAttributeData)
140        ));
141    }
142
143    #[test]
144    fn channel_data_parse_empty() {
145        let data = [0x40, 0x00, 0x00, 0x00];
146        let channel = ChannelData::parse(&data).unwrap();
147        assert_eq!(channel.data(), &[]);
148    }
149
150    #[test]
151    fn channel_data_parse_truncated_data() {
152        let data = [0x40, 0x00, 0x00, 0x01];
153        let Err(StunParseError::Truncated { expected, actual }) = ChannelData::parse(&data) else {
154            unreachable!();
155        };
156        assert_eq!(expected, 5);
157        assert_eq!(actual, 4);
158        assert_eq!(ChannelData::parse_header(&data).unwrap(), (0x4000, 1));
159    }
160
161    #[test]
162    fn channel_data_parse_truncated_header() {
163        let data = [0x40, 0x00, 0x00];
164        let Err(StunParseError::Truncated { expected, actual }) = ChannelData::parse(&data) else {
165            unreachable!();
166        };
167        assert_eq!(expected, 4);
168        assert_eq!(actual, 3);
169    }
170
171    static CHANNEL_SINGLE_BYTE: [u8; 5] = [0x40, 0x00, 0x00, 0x01, 0x42];
172
173    #[test]
174    fn channel_data_parse_success() {
175        let channel = ChannelData::parse(&CHANNEL_SINGLE_BYTE).unwrap();
176        assert_eq!(channel.data(), &[0x42]);
177        assert_eq!(channel.as_ref(), &[0x42]);
178    }
179
180    #[test]
181    fn channel_data_display() {
182        let channel = ChannelData::parse(&CHANNEL_SINGLE_BYTE).unwrap();
183        assert_eq!(
184            &alloc::format!("{channel}"),
185            "ChannelData(id: 16384, data of 1 bytes)"
186        );
187    }
188}