1use 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#[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 pub fn padding(&self) -> Option<u8> {
51 parser::parse_padding(self.data)
52 }
53
54 pub fn chunks(&'a self) -> impl Iterator<Item = &'a SdesChunk<'a>> {
56 self.chunks.iter()
57 }
58
59 pub fn builder() -> SdesBuilder<'a> {
61 SdesBuilder::default()
62 }
63}
64
65#[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 pub fn ssrc(&self) -> u32 {
122 self.ssrc
123 }
124
125 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 pub fn items(&'a self) -> impl Iterator<Item = &'a SdesItem<'a>> {
133 self.items.iter()
134 }
135
136 pub fn builder(ssrc: u32) -> SdesChunkBuilder<'a> {
138 SdesChunkBuilder::new(ssrc)
139 }
140}
141
142#[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;
153 pub const NAME: u8 = 0x02;
155 pub const EMAIL: u8 = 0x03;
157 pub const PHONE: u8 = 0x04;
159 pub const LOC: u8 = 0x05;
161 pub const TOOL: u8 = 0x06;
163 pub const NOTE: u8 = 0x07;
165 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 pub fn type_(&self) -> u8 {
217 self.data[0]
218 }
219
220 pub fn length(&self) -> usize {
222 self.data[1] as usize
223 }
224
225 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 pub fn get_value_string(&self) -> Result<String, std::string::FromUtf8Error> {
237 String::from_utf8(self.value().into())
238 }
239
240 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 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 pub fn builder(type_: u8, value: &'a str) -> SdesItemBuilder<'a> {
273 SdesItemBuilder::new(type_, value)
274 }
275}
276
277#[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 pub fn padding(mut self, padding: u8) -> Self {
289 self.padding = padding;
290 self
291 }
292
293 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 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 #[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#[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 pub fn add_item(mut self, item: SdesItemBuilder<'a>) -> Self {
374 self.items.push(item);
375 self
376 }
377
378 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 #[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 let end = pad_to_4bytes(idx + 1);
411 if end > idx {
412 buf[idx..end].fill(0);
413 }
414
415 end
416 }
417
418 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 pub fn add_item_owned(mut self, item: SdesItemBuilder<'_>) -> Self {
437 self.items.push(item.into_owned());
438 self
439 }
440}
441
442#[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 pub fn prefix(mut self, prefix: impl Into<Cow<'a, [u8]>>) -> Self {
464 self.prefix = prefix.into();
465 self
466 }
467
468 fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
474 let value_len = self.value.len();
475
476 if self.type_ == SdesItem::PRIV {
477 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 #[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 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 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 + 2 + 4 );
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 + 2 + 4 );
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 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}