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