xref: /webrtc/rtcp/src/source_description/mod.rs (revision bca24136)
1 #[cfg(test)]
2 mod source_description_test;
3 
4 use crate::{error::Error, header::*, packet::*, util::*};
5 use util::marshal::{Marshal, MarshalSize, Unmarshal};
6 
7 use bytes::{Buf, BufMut, Bytes};
8 use std::any::Any;
9 use std::fmt;
10 
11 type Result<T> = std::result::Result<T, util::Error>;
12 
13 const SDES_SOURCE_LEN: usize = 4;
14 const SDES_TYPE_LEN: usize = 1;
15 const SDES_TYPE_OFFSET: usize = 0;
16 const SDES_OCTET_COUNT_LEN: usize = 1;
17 const SDES_OCTET_COUNT_OFFSET: usize = 1;
18 const SDES_MAX_OCTET_COUNT: usize = (1 << 8) - 1;
19 const SDES_TEXT_OFFSET: usize = 2;
20 
21 /// SDESType is the item type used in the RTCP SDES control packet.
22 /// RTP SDES item types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-5
23 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
24 #[repr(u8)]
25 pub enum SdesType {
26     SdesEnd = 0,      // end of SDES list                RFC 3550, 6.5
27     SdesCname = 1,    // canonical name                  RFC 3550, 6.5.1
28     SdesName = 2,     // user name                       RFC 3550, 6.5.2
29     SdesEmail = 3,    // user's electronic mail address  RFC 3550, 6.5.3
30     SdesPhone = 4,    // user's phone number             RFC 3550, 6.5.4
31     SdesLocation = 5, // geographic user location        RFC 3550, 6.5.5
32     SdesTool = 6,     // name of application or tool     RFC 3550, 6.5.6
33     SdesNote = 7,     // notice about the source         RFC 3550, 6.5.7
34     SdesPrivate = 8,  // private extensions              RFC 3550, 6.5.8  (not implemented)
35 }
36 
37 impl Default for SdesType {
38     fn default() -> Self {
39         SdesType::SdesEnd
40     }
41 }
42 
43 impl fmt::Display for SdesType {
44     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45         let s = match self {
46             SdesType::SdesEnd => "END",
47             SdesType::SdesCname => "CNAME",
48             SdesType::SdesName => "NAME",
49             SdesType::SdesEmail => "EMAIL",
50             SdesType::SdesPhone => "PHONE",
51             SdesType::SdesLocation => "LOC",
52             SdesType::SdesTool => "TOOL",
53             SdesType::SdesNote => "NOTE",
54             SdesType::SdesPrivate => "PRIV",
55         };
56         write!(f, "{}", s)
57     }
58 }
59 
60 impl From<u8> for SdesType {
61     fn from(b: u8) -> Self {
62         match b {
63             1 => SdesType::SdesCname,
64             2 => SdesType::SdesName,
65             3 => SdesType::SdesEmail,
66             4 => SdesType::SdesPhone,
67             5 => SdesType::SdesLocation,
68             6 => SdesType::SdesTool,
69             7 => SdesType::SdesNote,
70             8 => SdesType::SdesPrivate,
71             _ => SdesType::SdesEnd,
72         }
73     }
74 }
75 
76 /// A SourceDescriptionChunk contains items describing a single RTP source
77 #[derive(Debug, PartialEq, Eq, Default, Clone)]
78 pub struct SourceDescriptionChunk {
79     /// The source (ssrc) or contributing source (csrc) identifier this packet describes
80     pub source: u32,
81     pub items: Vec<SourceDescriptionItem>,
82 }
83 
84 impl SourceDescriptionChunk {
85     fn raw_size(&self) -> usize {
86         let mut len = SDES_SOURCE_LEN;
87         for it in &self.items {
88             len += it.marshal_size();
89         }
90         len += SDES_TYPE_LEN; // for terminating null octet
91         len
92     }
93 }
94 
95 impl MarshalSize for SourceDescriptionChunk {
96     fn marshal_size(&self) -> usize {
97         let l = self.raw_size();
98         // align to 32-bit boundary
99         l + get_padding_size(l)
100     }
101 }
102 
103 impl Marshal for SourceDescriptionChunk {
104     /// Marshal encodes the SourceDescriptionChunk in binary
105     fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
106         if buf.remaining_mut() < self.marshal_size() {
107             return Err(Error::BufferTooShort.into());
108         }
109         /*
110          *  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
111          *  |                          SSRC/CSRC_1                          |
112          *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
113          *  |                           SDES items                          |
114          *  |                              ...                              |
115          *  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
116          */
117 
118         buf.put_u32(self.source);
119 
120         for it in &self.items {
121             let n = it.marshal_to(buf)?;
122             buf = &mut buf[n..];
123         }
124 
125         // The list of items in each chunk MUST be terminated by one or more null octets
126         buf.put_u8(SdesType::SdesEnd as u8);
127 
128         // additional null octets MUST be included if needed to pad until the next 32-bit boundary
129         put_padding(buf, self.raw_size());
130         Ok(self.marshal_size())
131     }
132 }
133 
134 impl Unmarshal for SourceDescriptionChunk {
135     /// Unmarshal decodes the SourceDescriptionChunk from binary
136     fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
137     where
138         Self: Sized,
139         B: Buf,
140     {
141         /*
142          *  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
143          *  |                          SSRC/CSRC_1                          |
144          *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
145          *  |                           SDES items                          |
146          *  |                              ...                              |
147          *  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
148          */
149         let raw_packet_len = raw_packet.remaining();
150         if raw_packet_len < (SDES_SOURCE_LEN + SDES_TYPE_LEN) {
151             return Err(Error::PacketTooShort.into());
152         }
153 
154         let source = raw_packet.get_u32();
155 
156         let mut offset = SDES_SOURCE_LEN;
157         let mut items = vec![];
158         while offset < raw_packet_len {
159             let item = SourceDescriptionItem::unmarshal(raw_packet)?;
160             if item.sdes_type == SdesType::SdesEnd {
161                 // offset + 1 (one byte for SdesEnd)
162                 let padding_len = get_padding_size(offset + 1);
163                 if raw_packet.remaining() >= padding_len {
164                     raw_packet.advance(padding_len);
165                     return Ok(SourceDescriptionChunk { source, items });
166                 } else {
167                     return Err(Error::PacketTooShort.into());
168                 }
169             }
170             offset += item.marshal_size();
171             items.push(item);
172         }
173 
174         Err(Error::PacketTooShort.into())
175     }
176 }
177 
178 /// A SourceDescriptionItem is a part of a SourceDescription that describes a stream.
179 #[derive(Debug, PartialEq, Eq, Default, Clone)]
180 pub struct SourceDescriptionItem {
181     /// The type identifier for this item. eg, SDESCNAME for canonical name description.
182     ///
183     /// Type zero or SDESEnd is interpreted as the end of an item list and cannot be used.
184     pub sdes_type: SdesType,
185     /// Text is a unicode text blob associated with the item. Its meaning varies based on the item's Type.
186     pub text: Bytes,
187 }
188 
189 impl MarshalSize for SourceDescriptionItem {
190     fn marshal_size(&self) -> usize {
191         /*
192          *   0                   1                   2                   3
193          *   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
194          *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
195          *  |    CNAME=1    |     length    | user and domain name        ...
196          *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
197          */
198         SDES_TYPE_LEN + SDES_OCTET_COUNT_LEN + self.text.len()
199     }
200 }
201 
202 impl Marshal for SourceDescriptionItem {
203     /// Marshal encodes the SourceDescriptionItem in binary
204     fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
205         /*
206          *   0                   1                   2                   3
207          *   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
208          *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
209          *  |    CNAME=1    |     length    | user and domain name        ...
210          *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
211          */
212 
213         if self.sdes_type == SdesType::SdesEnd {
214             return Err(Error::SdesMissingType.into());
215         }
216 
217         if buf.remaining_mut() < self.marshal_size() {
218             return Err(Error::BufferTooShort.into());
219         }
220 
221         buf.put_u8(self.sdes_type as u8);
222 
223         if self.text.len() > SDES_MAX_OCTET_COUNT {
224             return Err(Error::SdesTextTooLong.into());
225         }
226         buf.put_u8(self.text.len() as u8);
227         buf.put(self.text.clone());
228 
229         //no padding for each SourceDescriptionItem
230         Ok(self.marshal_size())
231     }
232 }
233 
234 impl Unmarshal for SourceDescriptionItem {
235     /// Unmarshal decodes the SourceDescriptionItem from binary
236     fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
237     where
238         Self: Sized,
239         B: Buf,
240     {
241         /*
242          *   0                   1                   2                   3
243          *   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
244          *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
245          *  |    CNAME=1    |     length    | user and domain name        ...
246          *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247          */
248         let raw_packet_len = raw_packet.remaining();
249         if raw_packet_len < SDES_TYPE_LEN {
250             return Err(Error::PacketTooShort.into());
251         }
252 
253         let sdes_type = SdesType::from(raw_packet.get_u8());
254         if sdes_type == SdesType::SdesEnd {
255             return Ok(SourceDescriptionItem {
256                 sdes_type,
257                 text: Bytes::new(),
258             });
259         }
260 
261         if raw_packet_len < (SDES_TYPE_LEN + SDES_OCTET_COUNT_LEN) {
262             return Err(Error::PacketTooShort.into());
263         }
264 
265         let octet_count = raw_packet.get_u8() as usize;
266         if SDES_TEXT_OFFSET + octet_count > raw_packet_len {
267             return Err(Error::PacketTooShort.into());
268         }
269 
270         let text = raw_packet.copy_to_bytes(octet_count);
271 
272         Ok(SourceDescriptionItem { sdes_type, text })
273     }
274 }
275 
276 /// A SourceDescription (SDES) packet describes the sources in an RTP stream.
277 #[derive(Debug, Default, PartialEq, Eq, Clone)]
278 pub struct SourceDescription {
279     pub chunks: Vec<SourceDescriptionChunk>,
280 }
281 
282 impl fmt::Display for SourceDescription {
283     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284         let mut out = "Source Description:\n".to_string();
285         for c in &self.chunks {
286             out += format!("\t{:x}\n", c.source).as_str();
287             for it in &c.items {
288                 out += format!("\t\t{:?}\n", it).as_str();
289             }
290         }
291         write!(f, "{}", out)
292     }
293 }
294 
295 impl Packet for SourceDescription {
296     /// Header returns the Header associated with this packet.
297     fn header(&self) -> Header {
298         Header {
299             padding: get_padding_size(self.raw_size()) != 0,
300             count: self.chunks.len() as u8,
301             packet_type: PacketType::SourceDescription,
302             length: ((self.marshal_size() / 4) - 1) as u16,
303         }
304     }
305 
306     /// destination_ssrc returns an array of SSRC values that this packet refers to.
307     fn destination_ssrc(&self) -> Vec<u32> {
308         self.chunks.iter().map(|x| x.source).collect()
309     }
310 
311     fn raw_size(&self) -> usize {
312         let mut chunks_length = 0;
313         for c in &self.chunks {
314             chunks_length += c.marshal_size();
315         }
316 
317         HEADER_LENGTH + chunks_length
318     }
319 
320     fn as_any(&self) -> &(dyn Any + Send + Sync) {
321         self
322     }
323 
324     fn equal(&self, other: &(dyn Packet + Send + Sync)) -> bool {
325         other
326             .as_any()
327             .downcast_ref::<SourceDescription>()
328             .map_or(false, |a| self == a)
329     }
330 
331     fn cloned(&self) -> Box<dyn Packet + Send + Sync> {
332         Box::new(self.clone())
333     }
334 }
335 
336 impl MarshalSize for SourceDescription {
337     fn marshal_size(&self) -> usize {
338         let l = self.raw_size();
339         // align to 32-bit boundary
340         l + get_padding_size(l)
341     }
342 }
343 
344 impl Marshal for SourceDescription {
345     /// Marshal encodes the SourceDescription in binary
346     fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
347         if self.chunks.len() > COUNT_MAX {
348             return Err(Error::TooManyChunks.into());
349         }
350 
351         if buf.remaining_mut() < self.marshal_size() {
352             return Err(Error::BufferTooShort.into());
353         }
354 
355         /*
356          *         0                   1                   2                   3
357          *         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
358          *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
359          * header |V=2|P|    SC   |  PT=SDES=202  |             length            |
360          *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
361          * chunk  |                          SSRC/CSRC_1                          |
362          *   1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363          *        |                           SDES items                          |
364          *        |                              ...                              |
365          *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
366          * chunk  |                          SSRC/CSRC_2                          |
367          *   2    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
368          *        |                           SDES items                          |
369          *        |                              ...                              |
370          *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
371          */
372 
373         let h = self.header();
374         let n = h.marshal_to(buf)?;
375         buf = &mut buf[n..];
376 
377         for c in &self.chunks {
378             let n = c.marshal_to(buf)?;
379             buf = &mut buf[n..];
380         }
381 
382         if h.padding {
383             put_padding(buf, self.raw_size());
384         }
385 
386         Ok(self.marshal_size())
387     }
388 }
389 
390 impl Unmarshal for SourceDescription {
391     /// Unmarshal decodes the SourceDescription from binary
392     fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
393     where
394         Self: Sized,
395         B: Buf,
396     {
397         /*
398          *         0                   1                   2                   3
399          *         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
400          *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401          * header |V=2|P|    SC   |  PT=SDES=202  |             length            |
402          *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
403          * chunk  |                          SSRC/CSRC_1                          |
404          *   1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405          *        |                           SDES items                          |
406          *        |                              ...                              |
407          *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
408          * chunk  |                          SSRC/CSRC_2                          |
409          *   2    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
410          *        |                           SDES items                          |
411          *        |                              ...                              |
412          *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
413          */
414         let raw_packet_len = raw_packet.remaining();
415 
416         let h = Header::unmarshal(raw_packet)?;
417         if h.packet_type != PacketType::SourceDescription {
418             return Err(Error::WrongType.into());
419         }
420 
421         let mut offset = HEADER_LENGTH;
422         let mut chunks = vec![];
423         while offset < raw_packet_len {
424             let chunk = SourceDescriptionChunk::unmarshal(raw_packet)?;
425             offset += chunk.marshal_size();
426             chunks.push(chunk);
427         }
428 
429         if chunks.len() != h.count as usize {
430             return Err(Error::InvalidHeader.into());
431         }
432 
433         if
434         /*h.padding &&*/
435         raw_packet.has_remaining() {
436             raw_packet.advance(raw_packet.remaining());
437         }
438 
439         Ok(SourceDescription { chunks })
440     }
441 }
442