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<'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 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;
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 pub fn type_(&self) -> u8 {
209 self.data[0]
210 }
211
212 pub fn length(&self) -> usize {
214 self.data[1] as usize
215 }
216
217 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 pub fn get_value_string(&self) -> Result<String, std::string::FromUtf8Error> {
229 String::from_utf8(self.value().into())
230 }
231
232 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 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 pub fn builder(type_: u8, value: &'a str) -> SdesItemBuilder<'a> {
265 SdesItemBuilder::new(type_, value)
266 }
267}
268
269#[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 pub fn padding(mut self, padding: u8) -> Self {
281 self.padding = padding;
282 self
283 }
284
285 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 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 #[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#[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 pub fn add_item(mut self, item: SdesItemBuilder<'a>) -> Self {
366 self.items.push(item);
367 self
368 }
369
370 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 #[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 let end = pad_to_4bytes(idx + 1);
403 if end > idx {
404 buf[idx..end].fill(0);
405 }
406
407 end
408 }
409
410 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 pub fn add_item_owned(mut self, item: SdesItemBuilder<'_>) -> Self {
429 self.items.push(item.into_owned());
430 self
431 }
432}
433
434#[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 pub fn prefix(mut self, prefix: impl Into<Cow<'a, [u8]>>) -> Self {
456 self.prefix = prefix.into();
457 self
458 }
459
460 fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
466 let value_len = self.value.as_bytes().len();
467
468 if self.type_ == SdesItem::PRIV {
469 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 #[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 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 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 + 2 + 4 );
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 + 2 + 4 );
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 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}