1use stun_types::message::StunParseError;
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
21pub struct ChannelData<'a> {
22 id: u16,
23 data: &'a [u8],
24}
25
26impl<'a> ChannelData<'a> {
27 pub fn new(id: u16, data: &'a [u8]) -> Self {
29 Self { id, data }
30 }
31
32 pub fn id(&self) -> u16 {
34 self.id
35 }
36
37 pub fn data(&self) -> &[u8] {
39 self.data
40 }
41
42 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 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 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}