rtcp_types/
sdes.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use std::borrow::Cow;
4use std::marker::PhantomData;
5
6use crate::{
7    prelude::*,
8    utils::{pad_to_4bytes, parser, u32_from_be_bytes, writer},
9    RtcpPacket, RtcpParseError, RtcpWriteError,
10};
11
12/// A Parsed Sdes packet.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Sdes<'a> {
15    data: &'a [u8],
16    chunks: Vec<SdesChunk<'a>>,
17}
18
19impl RtcpPacket for Sdes<'_> {
20    const MIN_PACKET_LEN: usize = 4;
21    const PACKET_TYPE: u8 = 202;
22}
23
24impl<'a> RtcpPacketParser<'a> for Sdes<'a> {
25    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
26        parser::check_packet::<Self>(data)?;
27
28        let mut chunks = vec![];
29        if data.len() > Self::MIN_PACKET_LEN {
30            let mut offset = Self::MIN_PACKET_LEN;
31
32            while offset < data.len() {
33                let (chunk, end) = SdesChunk::parse(&data[offset..])?;
34                offset += end;
35                chunks.push(chunk);
36            }
37        }
38
39        Ok(Self { data, chunks })
40    }
41
42    #[inline(always)]
43    fn header_data(&self) -> [u8; 4] {
44        self.data[..4].try_into().unwrap()
45    }
46}
47
48impl<'a> Sdes<'a> {
49    /// The (optional) padding used by this [`Sdes`] packet
50    pub fn padding(&self) -> Option<u8> {
51        parser::parse_padding(self.data)
52    }
53
54    /// The chunks contained in this SDES
55    pub fn chunks(&'a self) -> impl Iterator<Item = &'a SdesChunk<'a>> {
56        self.chunks.iter()
57    }
58
59    /// Create a new [`SdesBuilder`]
60    pub fn builder() -> SdesBuilder<'a> {
61        SdesBuilder::default()
62    }
63}
64
65/// A SDES chunk containing a single SSRC with possibly multiple SDES items
66#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct SdesChunk<'a> {
68    ssrc: u32,
69    items: Vec<SdesItem<'a>>,
70}
71
72impl<'a> SdesChunk<'a> {
73    const MIN_LEN: usize = 4;
74
75    fn new(ssrc: u32) -> Self {
76        Self {
77            ssrc,
78            items: Vec::new(),
79        }
80    }
81
82    fn parse(data: &'a [u8]) -> Result<(Self, usize), RtcpParseError> {
83        if data.len() < Self::MIN_LEN {
84            return Err(RtcpParseError::Truncated {
85                expected: Self::MIN_LEN,
86                actual: data.len(),
87            });
88        }
89
90        let mut ret = Self::new(u32_from_be_bytes(&data[0..4]));
91
92        let mut offset = Self::MIN_LEN;
93        if data.len() > Self::MIN_LEN {
94            while offset < data.len() {
95                if data[offset] == 0 {
96                    offset += 1;
97                    break;
98                }
99
100                let (item, end) = SdesItem::parse(&data[offset..])?;
101                offset += end;
102                ret.items.push(item);
103            }
104
105            while offset < data.len() && data[offset] == 0 {
106                offset += 1;
107            }
108        }
109
110        if pad_to_4bytes(offset) != offset {
111            return Err(RtcpParseError::Truncated {
112                expected: pad_to_4bytes(offset),
113                actual: offset,
114            });
115        }
116
117        Ok((ret, offset))
118    }
119
120    /// The SSRC that this chunk describes
121    pub fn ssrc(&self) -> u32 {
122        self.ssrc
123    }
124
125    /// The length of this chunk
126    pub fn length(&self) -> usize {
127        let len = Self::MIN_LEN + self.items.iter().fold(0, |acc, item| acc + item.length());
128        pad_to_4bytes(len)
129    }
130
131    /// The items in this chunk
132    pub fn items(&'a self) -> impl Iterator<Item = &'a SdesItem<'a>> {
133        self.items.iter()
134    }
135
136    /// Create a new [`SdesChunkBuilder`]
137    pub fn builder(ssrc: u32) -> SdesChunkBuilder<'a> {
138        SdesChunkBuilder::new(ssrc)
139    }
140}
141
142/// An SDES item
143#[derive(Clone, Debug, Eq, PartialEq)]
144pub struct SdesItem<'a> {
145    data: &'a [u8],
146}
147
148impl<'a> SdesItem<'a> {
149    const MIN_LEN: usize = 4;
150    const VALUE_MAX_LEN: u8 = 255;
151    /// The CNAME item in a SDES.
152    pub const CNAME: u8 = 0x01;
153    /// The NAME item in a SDES.
154    pub const NAME: u8 = 0x02;
155    /// The EMAIL item in a SDES.
156    pub const EMAIL: u8 = 0x03;
157    /// The PHONE item in a SDES.
158    pub const PHONE: u8 = 0x04;
159    /// The LOC item in a SDES.
160    pub const LOC: u8 = 0x05;
161    /// The TOOL item in a SDES.
162    pub const TOOL: u8 = 0x06;
163    /// The NOTE item in a SDES.
164    pub const NOTE: u8 = 0x07;
165    /// The PRIV item in a SDES.
166    pub const PRIV: u8 = 0x08;
167
168    fn parse(data: &'a [u8]) -> Result<(Self, usize), RtcpParseError> {
169        if data.len() < Self::MIN_LEN {
170            return Err(RtcpParseError::Truncated {
171                expected: Self::MIN_LEN,
172                actual: data.len(),
173            });
174        }
175
176        let length = data[1] as usize;
177        let end = 2 + length;
178        if end > data.len() {
179            return Err(RtcpParseError::Truncated {
180                expected: end,
181                actual: data.len(),
182            });
183        }
184
185        if length > Self::VALUE_MAX_LEN as usize {
186            return Err(RtcpParseError::SdesValueTooLarge {
187                len: length,
188                max: Self::VALUE_MAX_LEN,
189            });
190        }
191
192        let item = Self { data: &data[..end] };
193
194        if item.type_() == Self::PRIV {
195            if item.data.len() < 3 {
196                return Err(RtcpParseError::Truncated {
197                    expected: 3,
198                    actual: item.data.len(),
199                });
200            }
201            let prefix_len = item.priv_prefix_len();
202            let value_offset = item.priv_value_offset();
203
204            if value_offset as usize > data.len() {
205                return Err(RtcpParseError::SdesPrivPrefixTooLarge {
206                    len: prefix_len as usize,
207                    available: length as u8 - 1,
208                });
209            }
210        }
211
212        Ok((item, end))
213    }
214
215    /// The type of this item
216    pub fn type_(&self) -> u8 {
217        self.data[0]
218    }
219
220    /// The length of this item
221    pub fn length(&self) -> usize {
222        self.data[1] as usize
223    }
224
225    /// The value of this item
226    pub fn value(&self) -> &[u8] {
227        if self.type_() == Self::PRIV {
228            let offset = self.priv_value_offset() as usize;
229            &self.data[offset..]
230        } else {
231            &self.data[2..]
232        }
233    }
234
235    /// The value of this item as a string
236    pub fn get_value_string(&self) -> Result<String, std::string::FromUtf8Error> {
237        String::from_utf8(self.value().into())
238    }
239
240    /// Gets the prefix length of this PRIV SDES Item.
241    ///
242    /// # Panic
243    ///
244    /// Panics if the SDES Iem is not a PRIV.
245    pub fn priv_prefix_len(&self) -> u8 {
246        if self.type_() != Self::PRIV {
247            panic!("Item is not a PRIV");
248        }
249
250        self.data[2]
251    }
252
253    fn priv_value_offset(&self) -> u16 {
254        debug_assert!(self.type_() == Self::PRIV);
255        self.priv_prefix_len() as u16 + 3
256    }
257
258    /// Gets the prefix of this PRIV SDES Item.
259    ///
260    /// # Panic
261    ///
262    /// Panics if the SDES Iem is not a PRIV.
263    pub fn priv_prefix(&self) -> &[u8] {
264        if self.type_() != Self::PRIV {
265            panic!("Item is not a PRIV");
266        }
267
268        &self.data[3..3 + self.priv_prefix_len() as usize]
269    }
270
271    /// Create a new [`SdesItemBuilder`]
272    pub fn builder(type_: u8, value: &'a str) -> SdesItemBuilder<'a> {
273        SdesItemBuilder::new(type_, value)
274    }
275}
276
277/// SDES packet Builder
278#[derive(Debug, Default)]
279#[must_use = "The builder must be built to be used"]
280pub struct SdesBuilder<'a> {
281    padding: u8,
282    chunks: Vec<SdesChunkBuilder<'a>>,
283    phantom: PhantomData<&'a SdesChunkBuilder<'a>>,
284}
285
286impl<'a> SdesBuilder<'a> {
287    /// Sets the number of padding bytes to use for this App.
288    pub fn padding(mut self, padding: u8) -> Self {
289        self.padding = padding;
290        self
291    }
292
293    /// Adds the provided [`SdesChunk`].
294    pub fn add_chunk(mut self, chunk: SdesChunkBuilder<'a>) -> Self {
295        self.chunks.push(chunk);
296        self
297    }
298}
299
300impl RtcpPacketWriter for SdesBuilder<'_> {
301    /// Calculates the size required to write this App packet.
302    ///
303    /// Returns an error if:
304    ///
305    /// * An Item presents an invalid size.
306    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
307        if self.chunks.len() > Sdes::MAX_COUNT as usize {
308            return Err(RtcpWriteError::TooManySdesChunks {
309                count: self.chunks.len(),
310                max: Sdes::MAX_COUNT,
311            });
312        }
313
314        writer::check_padding(self.padding)?;
315
316        let mut chunks_size = 0;
317        for chunk in self.chunks.iter() {
318            chunks_size += chunk.calculate_size()?;
319        }
320
321        Ok(Sdes::MIN_PACKET_LEN + chunks_size + self.padding as usize)
322    }
323
324    /// Writes this Sdes packet chunks into `buf` without any validity checks.
325    ///
326    /// Returns the number of bytes written.
327    ///
328    /// # Panic
329    ///
330    /// Panics if the buf is not large enough.
331    #[inline]
332    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
333        let mut idx =
334            writer::write_header_unchecked::<Sdes>(self.padding, self.chunks.len() as u8, buf);
335
336        for chunk in self.chunks.iter() {
337            idx += chunk.write_into_unchecked(&mut buf[idx..]);
338        }
339
340        idx += writer::write_padding_unchecked(self.padding, &mut buf[idx..]);
341
342        idx
343    }
344
345    fn get_padding(&self) -> Option<u8> {
346        if self.padding == 0 {
347            return None;
348        }
349
350        Some(self.padding)
351    }
352}
353
354/// SDES Chunk Builder
355#[derive(Debug)]
356#[must_use = "The builder must be built to be used"]
357pub struct SdesChunkBuilder<'a> {
358    ssrc: u32,
359    items: Vec<SdesItemBuilder<'a>>,
360    phantom: PhantomData<&'a SdesItemBuilder<'a>>,
361}
362
363impl<'a> SdesChunkBuilder<'a> {
364    fn new(ssrc: u32) -> Self {
365        SdesChunkBuilder {
366            ssrc,
367            items: Vec::new(),
368            phantom: PhantomData,
369        }
370    }
371
372    /// Add an item to this chunk
373    pub fn add_item(mut self, item: SdesItemBuilder<'a>) -> Self {
374        self.items.push(item);
375        self
376    }
377
378    /// Calculates the size required to write this App packet.
379    ///
380    /// Returns an error if:
381    ///
382    /// * An Item presents an invalid size.
383    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
384        let mut items_size = 0;
385        for item in self.items.iter() {
386            items_size += item.calculate_size()?;
387        }
388
389        Ok(pad_to_4bytes(4 + items_size + 1))
390    }
391
392    /// Writes this [`SdesChunk`] into `buf` without checking the buffer size.
393    ///
394    /// Returns the number of bytes written.
395    ///
396    /// # Panic
397    ///
398    /// Panics if the buf is not large enough.
399    #[inline]
400    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
401        buf[0..4].copy_from_slice(&self.ssrc.to_be_bytes());
402
403        let mut idx = 4;
404        for item in self.items.iter() {
405            idx += item.write_into_unchecked(&mut buf[idx..]);
406        }
407
408        // always have at least one padding nul byte at the end so it is possible to determine the
409        // end of this chunk
410        let end = pad_to_4bytes(idx + 1);
411        if end > idx {
412            buf[idx..end].fill(0);
413        }
414
415        end
416    }
417
418    /// Writes the SDES Chunk into `buf`.
419    ///
420    /// Returns an error if:
421    ///
422    /// * The buffer is too small.
423    /// * An Item generated an error.
424    pub fn write_into(&self, buf: &mut [u8]) -> Result<usize, RtcpWriteError> {
425        let req_size = self.calculate_size()?;
426        if buf.len() < req_size {
427            return Err(RtcpWriteError::OutputTooSmall(req_size));
428        }
429
430        Ok(self.write_into_unchecked(&mut buf[..req_size]))
431    }
432
433    /// Adds an item transforming it into an owned version first.
434    ///
435    /// Lifetime of `self` is unchanged even if `item` is shorter lived.
436    pub fn add_item_owned(mut self, item: SdesItemBuilder<'_>) -> Self {
437        self.items.push(item.into_owned());
438        self
439    }
440}
441
442/// SDES item builder
443#[derive(Debug)]
444#[must_use = "The builder must be built to be used"]
445pub struct SdesItemBuilder<'a> {
446    type_: u8,
447    prefix: Cow<'a, [u8]>,
448    value: Cow<'a, str>,
449}
450
451impl<'a> SdesItemBuilder<'a> {
452    fn new(type_: u8, value: impl Into<Cow<'a, str>>) -> SdesItemBuilder<'a> {
453        SdesItemBuilder {
454            type_,
455            prefix: Default::default(),
456            value: value.into(),
457        }
458    }
459
460    /// Adds a prefix to a PRIV SDES Item.
461    ///
462    /// Has no effect if the type is not `SdesItem::PRIV`.
463    pub fn prefix(mut self, prefix: impl Into<Cow<'a, [u8]>>) -> Self {
464        self.prefix = prefix.into();
465        self
466    }
467
468    /// Calculates the size required to write this App packet.
469    ///
470    /// Returns an error if:
471    ///
472    /// * An Item presents an invalid size.
473    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
474        let value_len = self.value.len();
475
476        if self.type_ == SdesItem::PRIV {
477            // Note RFC 3550 p. 42 doesn't specify the encoding for the prefix "string".
478            // We decided to allow any byte sequence with compliant length.
479
480            let prefix_len = self.prefix.len();
481
482            if prefix_len + 1 > SdesItem::VALUE_MAX_LEN as usize {
483                return Err(RtcpWriteError::SdesPrivPrefixTooLarge {
484                    len: prefix_len,
485                    max: SdesItem::VALUE_MAX_LEN - 1,
486                });
487            }
488
489            if prefix_len + 1 + value_len > SdesItem::VALUE_MAX_LEN as usize {
490                return Err(RtcpWriteError::SdesValueTooLarge {
491                    len: value_len,
492                    max: SdesItem::VALUE_MAX_LEN - 1 - prefix_len as u8,
493                });
494            }
495
496            Ok(3 + prefix_len + value_len)
497        } else {
498            if value_len > SdesItem::VALUE_MAX_LEN as usize {
499                return Err(RtcpWriteError::SdesValueTooLarge {
500                    len: value_len,
501                    max: SdesItem::VALUE_MAX_LEN,
502                });
503            }
504
505            Ok(2 + value_len)
506        }
507    }
508
509    /// Writes this [`SdesChunk`] into `buf` without any validity checks.
510    ///
511    /// Returns the number of bytes written.
512    ///
513    /// # Panic
514    ///
515    /// Panics if the buf is not large enough.
516    #[inline]
517    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
518        let value = self.value.as_bytes();
519        let value_len = value.len();
520
521        buf[0] = self.type_;
522
523        let mut end;
524        if self.type_ == SdesItem::PRIV {
525            let prefix_len = self.prefix.len();
526
527            buf[1] = (prefix_len + 1 + value_len) as u8;
528
529            buf[2] = prefix_len as u8;
530            end = prefix_len + 3;
531            buf[3..end].copy_from_slice(&self.prefix);
532
533            let idx = end;
534            end += value_len;
535            buf[idx..end].copy_from_slice(value);
536        } else {
537            buf[1] = value_len as u8;
538            end = value.len() + 2;
539            buf[2..end].copy_from_slice(value);
540        }
541
542        end
543    }
544
545    /// Writes the SDES Item into `buf`.
546    ///
547    /// Returns an error if:
548    ///
549    /// * The buffer is too small.
550    /// * An Item presents an invalid size.
551    pub fn write_into(&self, buf: &mut [u8]) -> Result<usize, RtcpWriteError> {
552        let req_size = self.calculate_size()?;
553        if buf.len() < req_size {
554            return Err(RtcpWriteError::OutputTooSmall(req_size));
555        }
556
557        Ok(self.write_into_unchecked(&mut buf[..req_size]))
558    }
559
560    /// Converts this `SdesItemBuilder` into an owned version.
561    ///
562    /// Clones the `value` (and `prefix` if applicable) if it is not already owned.
563    pub fn into_owned(self) -> SdesItemBuilder<'static> {
564        SdesItemBuilder {
565            type_: self.type_,
566            prefix: self.prefix.into_owned().into(),
567            value: self.value.into_owned().into(),
568        }
569    }
570}
571
572#[cfg(test)]
573mod tests {
574    use super::*;
575
576    #[test]
577    fn parse_empty_sdes_chunk() {
578        let (chunk, _) =
579            SdesChunk::parse(&[0x00, 0x01, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00]).unwrap();
580        assert_eq!(chunk.ssrc(), 0x00010203);
581
582        let mut items = chunk.items();
583        let item = items.next().unwrap();
584        assert_eq!(item.type_(), SdesItem::CNAME);
585        assert!(item.value().is_empty());
586
587        assert!(items.next().is_none());
588    }
589
590    #[test]
591    fn parse_empty_sdes() {
592        let data = [0x80, 0xca, 0x00, 0x00];
593        let sdes = Sdes::parse(&data).unwrap();
594        assert_eq!(sdes.version(), 2);
595        assert_eq!(sdes.count(), 0);
596        assert_eq!(sdes.chunks().count(), 0);
597    }
598
599    #[test]
600    fn sdes_item_multiple_of_4_has_zero_terminating() {
601        let ssrcs = [0x98765432, 0x67892345];
602        let sdes = Sdes::builder()
603            .add_chunk(
604                SdesChunk::builder(ssrcs[0]).add_item(SdesItem::builder(SdesItem::CNAME, "ab")),
605            )
606            .add_chunk(
607                SdesChunk::builder(ssrcs[1]).add_item(SdesItem::builder(SdesItem::CNAME, "cd")),
608            );
609        let mut data = [0; 256];
610        let len = sdes.write_into(&mut data).unwrap();
611        let data = &data[..len];
612        let parsed = Sdes::parse(data).unwrap();
613        for c in parsed.chunks() {
614            assert!(ssrcs.contains(&c.ssrc()));
615        }
616    }
617
618    #[test]
619    fn parse_cname_sdes() {
620        let data = [
621            0x81, 0xca, 0x00, 0x02, 0x91, 0x82, 0x73, 0x64, 0x01, 0x02, 0x30, 0x31,
622        ];
623        let sdes = Sdes::parse(&data).unwrap();
624        assert_eq!(sdes.version(), 2);
625        assert_eq!(sdes.count(), 1);
626
627        let mut chunks = sdes.chunks();
628        let chunk = chunks.next().unwrap();
629        assert_eq!(chunk.ssrc(), 0x91827364);
630
631        let mut items = chunk.items();
632        let item = items.next().unwrap();
633        assert_eq!(item.type_(), SdesItem::CNAME);
634        assert_eq!(item.value(), &[0x30, 0x31]);
635
636        assert!(items.next().is_none());
637    }
638
639    #[test]
640    fn parse_cname_name_single_sdes_chunk() {
641        let data = [
642            0x81, 0xca, 0x00, 0x0c, 0x12, 0x34, 0x56, 0x78, 0x01, 0x05, 0x63, 0x6e, 0x61, 0x6d,
643            0x65, 0x02, 0x09, 0x46, 0x72, 0x61, 0x6e, 0xc3, 0xa7, 0x6f, 0x69, 0x73, 0x08, 0x16,
644            0x0b, 0x70, 0x72, 0x69, 0x76, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x70, 0x72,
645            0x69, 0x76, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x00,
646        ];
647        let sdes = Sdes::parse(&data).unwrap();
648        assert_eq!(sdes.version(), 2);
649        assert_eq!(sdes.count(), 1);
650
651        let mut chunks = sdes.chunks();
652        let chunk = chunks.next().unwrap();
653        assert_eq!(chunk.ssrc(), 0x12345678);
654
655        let mut items = chunk.items();
656
657        let item = items.next().unwrap();
658        assert_eq!(item.type_(), SdesItem::CNAME);
659        assert_eq!(item.value(), b"cname");
660        assert_eq!(item.get_value_string().unwrap(), "cname");
661
662        let item = items.next().unwrap();
663        assert_eq!(item.type_(), SdesItem::NAME);
664        assert_eq!(
665            item.value(),
666            &[0x46, 0x72, 0x61, 0x6e, 0xc3, 0xa7, 0x6f, 0x69, 0x73]
667        );
668        assert_eq!(item.get_value_string().unwrap(), "François");
669
670        let item = items.next().unwrap();
671        assert_eq!(item.type_(), SdesItem::PRIV);
672        assert_eq!(item.priv_prefix(), b"priv-prefix");
673        assert_eq!(item.value(), b"priv-value");
674        assert_eq!(item.get_value_string().unwrap(), "priv-value");
675
676        assert!(items.next().is_none());
677    }
678
679    #[test]
680    fn build_cname_name_single_sdes_chunk() {
681        let chunk1 = SdesChunk::builder(0x12345678)
682            .add_item(SdesItem::builder(SdesItem::CNAME, "cname"))
683            .add_item(SdesItem::builder(SdesItem::NAME, "François"))
684            .add_item(
685                SdesItem::builder(SdesItem::PRIV, "priv-value").prefix(b"priv-prefix".as_ref()),
686            );
687
688        const REQ_LEN: usize = Sdes::MIN_PACKET_LEN
689            + SdesChunk::MIN_LEN
690            + pad_to_4bytes(
691                2 + "cname".len()
692                    + 2
693                    + "François".len()
694                    + 3
695                    + "priv-prefix".len()
696                    + "priv-value".len(),
697            );
698
699        let sdesb = Sdes::builder().add_chunk(chunk1);
700        let req_len = sdesb.calculate_size().unwrap();
701        assert_eq!(req_len, REQ_LEN);
702        let mut data = [0; REQ_LEN];
703
704        let len = sdesb.write_into(&mut data).unwrap();
705        assert_eq!(len, REQ_LEN);
706        assert_eq!(
707            data,
708            [
709                0x81, 0xca, 0x00, 0x0c, 0x12, 0x34, 0x56, 0x78, 0x01, 0x05, 0x63, 0x6e, 0x61, 0x6d,
710                0x65, 0x02, 0x09, 0x46, 0x72, 0x61, 0x6e, 0xc3, 0xa7, 0x6f, 0x69, 0x73, 0x08, 0x16,
711                0x0b, 0x70, 0x72, 0x69, 0x76, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x70, 0x72,
712                0x69, 0x76, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x00,
713            ]
714        );
715    }
716
717    #[test]
718    fn parse_multiple_sdes_chunks() {
719        let data = [
720            0x82, 0xca, 0x00, 0x0e, 0x12, 0x34, 0x56, 0x78, 0x01, 0x05, 0x63, 0x6e, 0x61, 0x6d,
721            0x65, 0x02, 0x09, 0x46, 0x72, 0x61, 0x6e, 0xc3, 0xa7, 0x6f, 0x69, 0x73, 0x00, 0x00,
722            0x34, 0x56, 0x78, 0x9a, 0x03, 0x09, 0x75, 0x73, 0x65, 0x72, 0x40, 0x68, 0x6f, 0x73,
723            0x74, 0x04, 0x0c, 0x2b, 0x33, 0x33, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33,
724            0x34, 0x00, 0x00, 0x00,
725        ];
726        let sdes = Sdes::parse(&data).unwrap();
727        assert_eq!(sdes.version(), 2);
728        assert_eq!(sdes.count(), 2);
729
730        let mut chunks = sdes.chunks();
731        let chunk = chunks.next().unwrap();
732        assert_eq!(chunk.ssrc(), 0x12345678);
733
734        let mut items = chunk.items();
735
736        let item = items.next().unwrap();
737        assert_eq!(item.type_(), SdesItem::CNAME);
738        assert_eq!(item.value(), b"cname");
739
740        let item = items.next().unwrap();
741        assert_eq!(item.type_(), SdesItem::NAME);
742        assert_eq!(
743            item.value(),
744            &[0x46, 0x72, 0x61, 0x6e, 0xc3, 0xa7, 0x6f, 0x69, 0x73]
745        );
746
747        let chunk = chunks.next().unwrap();
748        assert_eq!(chunk.ssrc(), 0x3456789a);
749
750        let mut items = chunk.items();
751
752        let item = items.next().unwrap();
753        assert_eq!(item.type_(), SdesItem::EMAIL);
754        assert_eq!(item.value(), b"user@host");
755
756        let item = items.next().unwrap();
757        assert_eq!(item.type_(), SdesItem::PHONE);
758        assert_eq!(item.value(), b"+33678901234");
759
760        assert!(items.next().is_none());
761    }
762
763    #[test]
764    fn build_multiple_sdes_chunks() {
765        let chunk1 = SdesChunk::builder(0x12345678)
766            .add_item(SdesItem::builder(SdesItem::CNAME, "cname"))
767            .add_item(SdesItem::builder(SdesItem::NAME, "François"));
768
769        let chunk2 = SdesChunk::builder(0x3456789a)
770            .add_item(SdesItem::builder(SdesItem::EMAIL, "user@host"))
771            .add_item(SdesItem::builder(SdesItem::PHONE, "+33678901234"));
772
773        const REQ_LEN: usize = Sdes::MIN_PACKET_LEN
774            + SdesChunk::MIN_LEN
775            + pad_to_4bytes(2 + "cname".len() + 2 + "François".len())
776            + SdesChunk::MIN_LEN
777            + pad_to_4bytes(2 + "user@host".len() + 2 + "+33678901234".len());
778
779        let sdesb = Sdes::builder().add_chunk(chunk1).add_chunk(chunk2);
780        let req_len = sdesb.calculate_size().unwrap();
781        assert_eq!(req_len, REQ_LEN);
782
783        let mut data = [0; REQ_LEN];
784        let len = sdesb.write_into(&mut data).unwrap();
785        assert_eq!(len, REQ_LEN);
786        assert_eq!(
787            data,
788            [
789                0x82, 0xca, 0x00, 0x0e, 0x12, 0x34, 0x56, 0x78, 0x01, 0x05, 0x63, 0x6e, 0x61, 0x6d,
790                0x65, 0x02, 0x09, 0x46, 0x72, 0x61, 0x6e, 0xc3, 0xa7, 0x6f, 0x69, 0x73, 0x00, 0x00,
791                0x34, 0x56, 0x78, 0x9a, 0x03, 0x09, 0x75, 0x73, 0x65, 0x72, 0x40, 0x68, 0x6f, 0x73,
792                0x74, 0x04, 0x0c, 0x2b, 0x33, 0x33, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33,
793                0x34, 0x00, 0x00, 0x00,
794            ]
795        );
796    }
797
798    #[test]
799    fn build_static_sdes() {
800        const REQ_LEN: usize = Sdes::MIN_PACKET_LEN
801            + SdesChunk::MIN_LEN
802            + pad_to_4bytes(2 + 5 /* cname */ + 2 + 4 /* name */);
803
804        let sdesb = {
805            let cname = "cname".to_string();
806            let name = "name".to_string();
807            let chunk1 = SdesChunk::builder(0x12345678)
808                .add_item(SdesItem::builder(SdesItem::CNAME, &cname).into_owned())
809                .add_item_owned(SdesItem::builder(SdesItem::NAME, &name));
810
811            Sdes::builder().add_chunk(chunk1)
812        };
813
814        let req_len = sdesb.calculate_size().unwrap();
815        assert_eq!(req_len, REQ_LEN);
816
817        let mut data = [0; REQ_LEN];
818        let len = sdesb.write_into(&mut data).unwrap();
819        assert_eq!(len, REQ_LEN);
820        assert_eq!(
821            data,
822            [
823                0x81, 0xca, 0x00, 0x05, 0x12, 0x34, 0x56, 0x78, 0x01, 0x05, 0x63, 0x6e, 0x61, 0x6d,
824                0x65, 0x02, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00,
825            ]
826        );
827    }
828
829    #[test]
830    fn build_sdes_from_shorter_lived_item() {
831        const REQ_LEN: usize = Sdes::MIN_PACKET_LEN
832            + SdesChunk::MIN_LEN
833            + pad_to_4bytes(2 + 5 /* cname */ + 2 + 4 /* name */);
834
835        let mut data = [0; REQ_LEN];
836
837        {
838            let cname = "cname".to_string();
839            let chunk1 = SdesChunk::builder(0x12345678)
840                .add_item(SdesItem::builder(SdesItem::CNAME, cname.as_str()));
841
842            let chunk1 = {
843                // adding name which is shorter lived than chunk1
844                let name = "name".to_string();
845                chunk1.add_item_owned(SdesItem::builder(SdesItem::NAME, name.as_str()))
846            };
847
848            let sdesb = Sdes::builder().add_chunk(chunk1);
849
850            let req_len = sdesb.calculate_size().unwrap();
851            assert_eq!(req_len, REQ_LEN);
852
853            let len = sdesb.write_into(&mut data).unwrap();
854            assert_eq!(len, REQ_LEN);
855        }
856
857        assert_eq!(
858            data,
859            [
860                0x81, 0xca, 0x00, 0x05, 0x12, 0x34, 0x56, 0x78, 0x01, 0x05, 0x63, 0x6e, 0x61, 0x6d,
861                0x65, 0x02, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00,
862            ]
863        );
864    }
865
866    #[test]
867    fn build_too_many_chunks() {
868        let mut b = Sdes::builder();
869        for _ in 0..Sdes::MAX_COUNT as usize + 1 {
870            b = b.add_chunk(SdesChunk::builder(0))
871        }
872        let err = b.calculate_size().unwrap_err();
873        assert_eq!(
874            err,
875            RtcpWriteError::TooManySdesChunks {
876                count: Sdes::MAX_COUNT as usize + 1,
877                max: Sdes::MAX_COUNT,
878            }
879        );
880    }
881
882    #[test]
883    fn build_item_value_too_large() {
884        let value: String =
885            String::from_utf8([b'a'; SdesItem::VALUE_MAX_LEN as usize + 1].into()).unwrap();
886        let b = Sdes::builder()
887            .add_chunk(SdesChunk::builder(0).add_item(SdesItem::builder(SdesItem::NAME, &value)));
888        let err = b.calculate_size().unwrap_err();
889        assert_eq!(
890            err,
891            RtcpWriteError::SdesValueTooLarge {
892                len: SdesItem::VALUE_MAX_LEN as usize + 1,
893                max: SdesItem::VALUE_MAX_LEN,
894            }
895        );
896    }
897
898    #[test]
899    fn build_priv_item_prefix_too_large() {
900        let prefix = vec![0x01; SdesItem::VALUE_MAX_LEN as usize];
901        let b = Sdes::builder().add_chunk(
902            SdesChunk::builder(0).add_item(SdesItem::builder(SdesItem::PRIV, "").prefix(&prefix)),
903        );
904        let err = b.calculate_size().unwrap_err();
905        assert_eq!(
906            err,
907            RtcpWriteError::SdesPrivPrefixTooLarge {
908                len: SdesItem::VALUE_MAX_LEN as usize,
909                max: SdesItem::VALUE_MAX_LEN - 1,
910            }
911        );
912    }
913
914    #[test]
915    fn build_priv_item_value_too_large() {
916        let value: String =
917            String::from_utf8([b'a'; SdesItem::VALUE_MAX_LEN as usize].into()).unwrap();
918        let b = Sdes::builder()
919            .add_chunk(SdesChunk::builder(0).add_item(SdesItem::builder(SdesItem::PRIV, &value)));
920        let err = b.calculate_size().unwrap_err();
921        assert_eq!(
922            err,
923            RtcpWriteError::SdesValueTooLarge {
924                len: SdesItem::VALUE_MAX_LEN as usize,
925                max: SdesItem::VALUE_MAX_LEN - 1,
926            }
927        );
928    }
929
930    #[test]
931    fn build_padding_not_multiple_4() {
932        let b = Sdes::builder().padding(5);
933        let err = b.calculate_size().unwrap_err();
934        assert_eq!(err, RtcpWriteError::InvalidPadding { padding: 5 });
935    }
936}