Skip to content
File

Blob: firmware/vendor/str0m/src/rtp/rtcp/xr.rs

rust286 lines
1use std::time::SystemTime;
2 
3use crate::util::SystemTimeExt;
4 
5use super::{FeedbackMessageType, RtcpType, Ssrc};
6use super::{RtcpHeader, RtcpPacket};
7 
8// 0 1 2 3
9// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
10// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11// |V=2|P|reserved | PT=XR=207 | length |
12// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13// | SSRC |
14// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15// : report blocks :
16// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17 
18/// Extended receiver report (XR).
19///
20/// RFC 3611: <https://datatracker.ietf.org/doc/html/rfc3611#page-21>
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ExtendedReport {
23 /// The SSRC this report is for.
24 pub ssrc: Ssrc,
25 /// The blocks reported.
26 pub blocks: Vec<ReportBlock>,
27}
28 
29/// Parts of an extended report XR.
30#[derive(Debug, Clone, PartialEq, Eq)]
31#[allow(missing_docs)]
32pub enum ReportBlock {
33 Rrtr(Rrtr),
34 Dlrr(Dlrr),
35}
36 
37// 0 1 2 3
38// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
39// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40// | BT=4 | reserved | block length = 2 |
41// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42// | NTP timestamp, most significant word |
43// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44// | NTP timestamp, least significant word |
45// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46 
47/// Receiver Reference Time Report Block.
48///
49/// <https://datatracker.ietf.org/doc/html/rfc3611#section-4.4>
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[allow(missing_docs)]
52pub struct Rrtr {
53 pub ntp_time: SystemTime,
54}
55 
56// 0 1 2 3
57// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
58// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
59// | BT=5 | reserved | block length |
60// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
61// | SSRC_1 (SSRC of first receiver) | sub-
62// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
63// | last RR (LRR) | 1
64// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65// | delay since last RR (DLRR) |
66// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
67// | SSRC_2 (SSRC of second receiver) | sub-
68// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
69// : ... : 2
70// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
71 
72/// DLRR Report Block
73///
74/// <https://datatracker.ietf.org/doc/html/rfc3611#section-4.5>
75#[derive(Debug, Clone, PartialEq, Eq)]
76#[allow(missing_docs)]
77pub struct Dlrr {
78 pub items: Vec<DlrrItem>,
79}
80 
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct DlrrItem {
83 pub ssrc: Ssrc,
84 pub last_rr_time: u32,
85 pub last_rr_delay: u32,
86}
87 
88impl RtcpPacket for ExtendedReport {
89 fn header(&self) -> RtcpHeader {
90 RtcpHeader {
91 rtcp_type: RtcpType::ExtendedReport,
92 feedback_message_type: FeedbackMessageType::NotUsed,
93 words_less_one: (self.length_words() - 1) as u16,
94 }
95 }
96 
97 fn length_words(&self) -> usize {
98 let header = 1;
99 let ssrc = 1;
100 let blocks: usize = self.blocks.iter().map(|b| b.len() / 4).sum();
101 header + ssrc + blocks
102 }
103 
104 fn write_to(&self, buf: &mut [u8]) -> usize {
105 let mut len = self.header().write_to(buf);
106 
107 buf[4..8].copy_from_slice(&self.ssrc.to_be_bytes());
108 len += 4;
109 
110 for block in self.blocks.iter() {
111 len += match block {
112 ReportBlock::Rrtr(b) => b.write_to(&mut buf[len..]),
113 ReportBlock::Dlrr(b) => b.write_to(&mut buf[len..]),
114 };
115 }
116 
117 len
118 }
119}
120 
121impl ReportBlock {
122 pub(crate) fn len(&self) -> usize {
123 match self {
124 Self::Rrtr(_) => Rrtr::len(),
125 Self::Dlrr(v) => v.len(),
126 }
127 }
128}
129 
130impl Rrtr {
131 fn write_to(&self, buf: &mut [u8]) -> usize {
132 // block type
133 buf[0] = 4_u8;
134 // reserved;
135 buf[1] = 0_u8;
136 // block length
137 buf[2..4].copy_from_slice(&2_u16.to_be_bytes());
138 
139 // NTP timestamp
140 let mt = self.ntp_time.as_ntp_64();
141 buf[4..12].copy_from_slice(&mt.to_be_bytes());
142 
143 12
144 }
145 
146 fn len() -> usize {
147 12
148 }
149}
150 
151impl Dlrr {
152 fn write_to(&self, buf: &mut [u8]) -> usize {
153 // block type
154 buf[0] = 5_u8;
155 // reserved;
156 buf[1] = 0_u8;
157 // block length, in 32-bit words (RFC 3611 §3): each DLRR sub-block is 3 words
158 // (ssrc + last_rr + delay = 12 bytes). Must match the parser, which divides this
159 // field by 3 to recover the item count.
160 let len: u16 = self.items.len() as u16 * 3_u16;
161 buf[2..4].copy_from_slice(&len.to_be_bytes());
162 
163 let mut buf = &mut buf[4..];
164 
165 for item in self.items.iter() {
166 buf[0..4].copy_from_slice(&item.ssrc.to_be_bytes());
167 buf[4..8].copy_from_slice(&item.last_rr_time.to_be_bytes());
168 buf[8..12].copy_from_slice(&item.last_rr_delay.to_be_bytes());
169 // advance past the full 12-byte sub-block
170 buf = &mut buf[12..];
171 }
172 
173 self.len()
174 }
175 
176 fn len(&self) -> usize {
177 4 + (self.items.len() * 4 * 3)
178 }
179}
180 
181impl<'a> TryFrom<&'a [u8]> for ExtendedReport {
182 type Error = &'static str;
183 
184 fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
185 if buf.len() < 4 {
186 return Err("Less than 4 bytes for ExtendedReport");
187 }
188 
189 let ssrc = u32::from_be_bytes(buf[..4].try_into().unwrap()).into();
190 
191 let mut blocks: Vec<ReportBlock> = Vec::new();
192 let mut buf = &buf[4..];
193 
194 while let Ok(block) = buf.try_into() {
195 let block: ReportBlock = block;
196 let len = block.len();
197 blocks.push(block);
198 // A block's reported len may exceed the remaining bytes for a
199 // truncated/malformed packet. Guard against slicing past the end
200 // (panic) and against a zero len that would spin forever.
201 if len == 0 || len >= buf.len() {
202 break;
203 }
204 buf = &buf[len..];
205 }
206 
207 Ok(ExtendedReport { ssrc, blocks })
208 }
209}
210 
211impl<'a> TryFrom<&'a [u8]> for ReportBlock {
212 type Error = &'static str;
213 
214 fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
215 if buf.is_empty() {
216 return Err("not enough data");
217 }
218 
219 let block_type: u8 = buf[0];
220 match block_type {
221 4 => {
222 let block = Rrtr::try_from(buf)?;
223 Ok(Self::Rrtr(block))
224 }
225 5 => {
226 let block = Dlrr::try_from(buf)?;
227 Ok(Self::Dlrr(block))
228 }
229 _ => Err("unknown block type"),
230 }
231 }
232}
233 
234impl<'a> TryFrom<&'a [u8]> for Rrtr {
235 type Error = &'static str;
236 
237 fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
238 if buf.len() < 12 {
239 return Err("Less than 12 bytes for Rrtr");
240 }
241 
242 let ntp_time = u64::from_be_bytes(buf[4..4 + 8].try_into().unwrap());
243 let ntp_time = SystemTime::from_ntp_64(ntp_time);
244 
245 Ok(Rrtr { ntp_time })
246 }
247}
248 
249impl<'a> TryFrom<&'a [u8]> for Dlrr {
250 type Error = &'static str;
251 
252 fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
253 if buf.len() < 4 {
254 return Err("Less than 4 bytes for Dlrr");
255 }
256 
257 let words_per_block = 3;
258 let blocks = u16::from_be_bytes(buf[2..4].try_into().unwrap()) / words_per_block;
259 
260 let mut items: Vec<DlrrItem> = Vec::with_capacity(blocks as usize);
261 
262 // move on after the header
263 let mut buf = &buf[4..];
264 
265 for _ in 0..blocks {
266 // Each DLRR item is 12 bytes. The block count comes from the
267 // (attacker-controlled) length field, so stop if there aren't
268 // enough bytes left for another item.
269 if buf.len() < 12 {
270 break;
271 }
272 let ssrc = u32::from_be_bytes(buf[0..4].try_into().unwrap()).into();
273 let last_rr_time = u32::from_be_bytes(buf[4..8].try_into().unwrap());
274 let last_rr_delay = u32::from_be_bytes(buf[8..12].try_into().unwrap());
275 items.push(DlrrItem {
276 ssrc,
277 last_rr_time,
278 last_rr_delay,
279 });
280 buf = &buf[12..];
281 }
282 
283 Ok(Dlrr { items })
284 }
285}