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