1use std::{
8 convert::TryFrom,
9 fmt::{Display, Write},
10 net::IpAddr,
11 str::FromStr,
12};
13
14use crate::{
15 builders, enums::*, parse_connection_address, parse_connection_ip_address, parser::is_token,
16 Attribute, ConnectionAddress, ConnectionAddressError,
17};
18
19pub trait TypedAttribute: Display + FromStr<Err = AttributeError> {
21 const NAME: &'static str;
22}
23
24impl<T: TypedAttribute> From<T> for Attribute {
25 fn from(attr: T) -> Attribute {
26 let value = attr.to_string();
27 Attribute {
28 attribute: T::NAME.to_string(),
29 value: (!value.is_empty()).then_some(value),
31 }
32 }
33}
34
35#[derive(Debug, PartialEq, Eq, thiserror::Error)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38pub enum AttributeError {
39 #[error("Attribute {} not found", .0)]
41 NotFound(String),
42 #[error("Param {} not found in {}", .param, .attr)]
44 ParamNotFound { param: String, attr: String },
45 #[error("Invalid value {} for Param {} in {}", .val ,.param, .attr)]
47 InvalidParamValue {
48 param: String,
49 val: String,
50 attr: String,
51 },
52 #[error("Unsupported attribute format: {} for {}", .val, .attr)]
54 UnsupportedFormat { val: String, attr: String },
55 #[error("Unexpected trailing item {} for in {}", .val, .attr)]
57 UnexpectedTrailingItem { val: String, attr: String },
58 #[error("{}: {}", .attr, .error)]
60 Other { error: String, attr: String },
61}
62
63impl AttributeError {
64 pub fn is_attribute_not_found(&self) -> bool {
65 matches!(self, AttributeError::NotFound(_))
66 }
67
68 fn add_param_context(&mut self, param_context: &str) {
70 use AttributeError::*;
71 match self {
72 NotFound(_)
73 | Other { .. }
74 | UnsupportedFormat { .. }
75 | UnexpectedTrailingItem { .. } => (),
76 ParamNotFound { param, .. } | InvalidParamValue { param, .. } => {
77 param.push(')');
78 param.insert_str(0, " (");
79 param.insert_str(0, param_context);
80 }
81 }
82 }
83
84 pub(crate) fn set_attr(&mut self, new_attr: impl ToString) {
86 use AttributeError::*;
87 match self {
88 NotFound(attr)
89 | ParamNotFound { attr, .. }
90 | InvalidParamValue { attr, .. }
91 | UnsupportedFormat { attr, .. }
92 | UnexpectedTrailingItem { attr, .. }
93 | Other { attr, .. } => *attr = new_attr.to_string(),
94 }
95 }
96}
97
98pub(crate) trait ErrorContext {
99 fn with_param_context(self, param_context: &str) -> Self;
100 fn with_attr(self, new_attr: impl ToString) -> Self;
101}
102
103impl<T> ErrorContext for Result<T, AttributeError> {
104 fn with_param_context(self, param_context: &str) -> Self {
106 self.map_err(|mut err| {
107 err.add_param_context(param_context);
108 err
109 })
110 }
111
112 fn with_attr(self, new_attr: impl ToString) -> Self {
114 self.map_err(|mut err| {
115 err.set_attr(new_attr);
116 err
117 })
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126pub struct RtpMap {
127 pub payload_type: u8,
129 pub encoding_name: String,
132 pub clock_rate: u32,
134 pub encoding_params: Option<String>,
138}
139
140impl RtpMap {
141 pub fn new(payload_type: u8, encoding_name: impl ToString, clock_rate: u32) -> Self {
142 RtpMap {
143 payload_type,
144 encoding_name: encoding_name.to_string(),
145 clock_rate,
146 encoding_params: None,
147 }
148 }
149
150 pub fn builder(
151 payload_type: u8,
152 encoding_name: impl ToString,
153 clock_rate: u32,
154 ) -> builders::RtpMap {
155 builders::RtpMap::new(payload_type, encoding_name, clock_rate)
156 }
157
158 pub fn with_encoding_params(
159 payload_type: u8,
160 encoding_name: impl ToString,
161 clock_rate: u32,
162 encoding_params: impl ToString,
163 ) -> Self {
164 RtpMap {
165 payload_type,
166 encoding_name: encoding_name.to_string(),
167 clock_rate,
168 encoding_params: Some(encoding_params.to_string()),
169 }
170 }
171
172 pub fn set_encoding_params(&mut self, encoding_params: impl ToString) {
173 self.encoding_params = Some(encoding_params.to_string());
174 }
175}
176
177impl FromStr for RtpMap {
178 type Err = AttributeError;
179
180 fn from_str(s: &str) -> Result<Self, Self::Err> {
181 let Some((pt, rest)) = s.split_once(' ') else {
182 return Err(AttributeError::UnsupportedFormat {
183 val: s.to_string(),
184 attr: <Self as TypedAttribute>::NAME.to_string(),
185 });
186 };
187
188 let Ok(pt) = pt.parse::<u8>() else {
189 return Err(AttributeError::InvalidParamValue {
190 param: "Payload type".to_string(),
191 val: pt.to_string(),
192 attr: <Self as TypedAttribute>::NAME.to_string(),
193 });
194 };
195
196 if pt > 127 {
197 return Err(AttributeError::InvalidParamValue {
198 param: "Payload type".to_string(),
199 val: format!("{pt}(expected 0-127)"),
200 attr: <Self as TypedAttribute>::NAME.to_string(),
201 });
202 }
203
204 let mut i = rest.splitn(3, '/');
205 let Some(encoding) = i.next() else {
206 return Err(AttributeError::ParamNotFound {
207 param: "Encoding name".to_string(),
208 attr: <Self as TypedAttribute>::NAME.to_string(),
209 });
210 };
211
212 let Some(clock_rate) = i.next() else {
213 return Err(AttributeError::ParamNotFound {
214 param: "Clock rate".to_string(),
215 attr: <Self as TypedAttribute>::NAME.to_string(),
216 });
217 };
218
219 let Ok(clock_rate) = clock_rate.parse::<u32>() else {
220 return Err(AttributeError::InvalidParamValue {
221 param: "Clock rate".to_string(),
222 val: clock_rate.to_string(),
223 attr: <Self as TypedAttribute>::NAME.to_string(),
224 });
225 };
226
227 let params = i.next().map(String::from);
228
229 Ok(Self {
230 payload_type: pt,
231 encoding_name: encoding.to_owned(),
232 clock_rate,
233 encoding_params: params,
234 })
235 }
236}
237
238impl Display for RtpMap {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 write!(
241 f,
242 "{} {}/{}",
243 self.payload_type, self.encoding_name, self.clock_rate
244 )?;
245 if let Some(params) = &self.encoding_params {
246 f.write_char('/')?;
247 f.write_str(params)?;
248 }
249 Ok(())
250 }
251}
252
253impl TypedAttribute for RtpMap {
254 const NAME: &'static str = "rtpmap";
255}
256
257#[derive(Debug, Clone, PartialEq, Eq)]
259#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
260pub struct FmtpParam {
261 pub param: String,
262 pub val: Option<String>,
263}
264
265impl FmtpParam {
266 pub fn new(param: impl ToString) -> Self {
267 FmtpParam {
268 param: param.to_string(),
269 val: None,
270 }
271 }
272
273 pub fn builder(param: impl ToString) -> builders::FmtpParam {
274 builders::FmtpParam::new(param)
275 }
276
277 pub fn with_value(param: impl ToString, value: impl ToString) -> Self {
278 FmtpParam {
279 param: param.to_string(),
280 val: Some(value.to_string()),
281 }
282 }
283
284 pub fn set_value(&mut self, value: impl ToString) {
285 self.val = Some(value.to_string());
286 }
287}
288
289#[derive(Debug, Clone, PartialEq, Eq)]
293#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
294pub struct Fmtp {
295 pub fmt: u8,
297 pub format_specific_params: Vec<FmtpParam>,
301}
302
303impl Fmtp {
304 pub fn new(fmt: u8) -> Self {
305 Fmtp {
306 fmt,
307 format_specific_params: vec![],
308 }
309 }
310
311 pub fn builder(fmt: u8) -> builders::Fmtp {
312 builders::Fmtp::new(fmt)
313 }
314
315 pub fn add_format_specific_param(&mut self, format_specific_param: FmtpParam) {
316 self.format_specific_params.push(format_specific_param)
317 }
318
319 pub fn add_format_specific_params(
320 &mut self,
321 format_specific_params: impl IntoIterator<Item = FmtpParam>,
322 ) {
323 self.format_specific_params.extend(format_specific_params)
324 }
325}
326
327impl FromStr for Fmtp {
328 type Err = AttributeError;
329
330 fn from_str(s: &str) -> Result<Self, Self::Err> {
331 let Some((fmt, rest)) = s.split_once(' ') else {
332 return Err(AttributeError::UnsupportedFormat {
333 val: s.to_string(),
334 attr: <Self as TypedAttribute>::NAME.to_string(),
335 });
336 };
337
338 let Ok(fmt) = fmt.parse::<u8>() else {
339 return Err(AttributeError::InvalidParamValue {
340 param: "fmtp".to_string(),
341 val: fmt.to_string(),
342 attr: <Self as TypedAttribute>::NAME.to_string(),
343 });
344 };
345
346 let mut params: Vec<FmtpParam> = Vec::new();
347 for param in rest.split(';') {
348 if let Some((key, value)) = param.split_once('=') {
349 params.push(FmtpParam {
350 param: key.to_string(),
351 val: Some(value.to_string()),
352 });
353 } else {
354 params.push(FmtpParam {
355 param: param.to_string(),
356 val: None,
357 });
358 }
359 }
360
361 Ok(Self {
362 fmt,
363 format_specific_params: params,
364 })
365 }
366}
367
368impl Display for Fmtp {
369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370 write!(f, "{} ", self.fmt)?;
371 let mut iter = self.format_specific_params.iter().peekable();
372 while let Some(p) = iter.next() {
373 write!(f, "{}", p.param)?;
374 if let Some(val) = &p.val {
375 f.write_char('=')?;
376 f.write_str(val.as_str())?;
377 }
378 if iter.peek().is_some() {
379 f.write_char(';')?;
380 }
381 }
382 Ok(())
383 }
384}
385
386impl TypedAttribute for Fmtp {
387 const NAME: &'static str = "fmtp";
388}
389
390#[derive(Debug, Clone, PartialEq, Eq)]
400#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
401pub struct Rtcp {
402 pub port: u16,
404 pub nettype: Option<NetType>,
408 pub addrtype: Option<AddrType>,
412 pub connection_address: Option<String>,
417}
418
419impl Rtcp {
420 pub fn with_port(port: u16) -> Self {
425 Rtcp {
426 port,
427 nettype: None,
428 addrtype: None,
429 connection_address: None,
430 }
431 }
432
433 pub fn with_ip_addr(port: u16, connection_address: impl Into<IpAddr>) -> Self {
435 let connection_address = connection_address.into();
436 Rtcp {
437 port,
438 nettype: Some(NetType::In),
439 addrtype: Some(connection_address.into()),
440 connection_address: Some(connection_address.to_string()),
441 }
442 }
443
444 pub fn new(
448 port: u16,
449 nettype: NetType,
450 addrtype: AddrType,
451 connection_address: impl ToString,
452 ) -> Self {
453 Rtcp {
454 port,
455 nettype: Some(nettype),
456 addrtype: Some(addrtype),
457 connection_address: Some(connection_address.to_string()),
458 }
459 }
460
461 #[deprecated(since = "0.3.1", note = "use Rtcp::try_address instead")]
470 pub fn try_parse_connection_ip_address(&self) -> Option<Result<IpAddr, &str>> {
471 self.connection_address
472 .as_deref()
473 .map(parse_connection_ip_address)
474 }
475
476 fn effective_addrtype(&self) -> Option<AddrType> {
482 self.addrtype.clone().or_else(|| {
483 let address = parse_connection_ip_address(self.connection_address.as_deref()?).ok()?;
484 Some(address.into())
485 })
486 }
487
488 pub fn try_address(&self) -> Option<Result<ConnectionAddress, ConnectionAddressError>> {
506 let address = self.connection_address.as_deref()?;
507 match self.effective_addrtype() {
508 Some(addrtype) => Some(parse_connection_address(address, &addrtype)),
509 None => Some(Err(ConnectionAddressError::InvalidAddress(
510 address.to_string(),
511 ))),
512 }
513 }
514
515 pub fn is_multicast(&self) -> bool {
520 matches!(self.connection_address.as_deref().map(parse_connection_ip_address),
521 Some(Ok(address)) if address.is_multicast())
522 }
523
524 pub fn try_is_multicast(&self) -> Option<Result<bool, ConnectionAddressError>> {
530 let address = self.connection_address.as_deref()?;
531 Some(
532 parse_connection_ip_address(address)
533 .map(|address| address.is_multicast())
534 .map_err(|_| ConnectionAddressError::InvalidAddress(address.to_string())),
535 )
536 }
537
538 pub fn is_ipv4(&self) -> bool {
543 matches!(self.connection_address.as_deref().map(parse_connection_ip_address),
544 Some(Ok(address)) if address.is_ipv4())
545 }
546
547 pub fn is_ipv6(&self) -> bool {
552 matches!(self.connection_address.as_deref().map(parse_connection_ip_address),
553 Some(Ok(address)) if address.is_ipv6())
554 }
555
556 pub fn set_connection_ip_address(&mut self, connection_address: impl Into<IpAddr>) {
560 let connection_address = connection_address.into();
561 self.nettype.get_or_insert(NetType::In);
562 self.addrtype = Some(connection_address.into());
563 self.connection_address = Some(connection_address.to_string());
564 }
565}
566
567impl FromStr for Rtcp {
568 type Err = AttributeError;
569
570 fn from_str(s: &str) -> Result<Self, Self::Err> {
571 let mut i = s.split(' ').filter(|part| !part.is_empty());
574 let Some(port) = i.next() else {
575 return Err(AttributeError::ParamNotFound {
576 param: "Port".to_string(),
577 attr: <Self as TypedAttribute>::NAME.to_string(),
578 });
579 };
580
581 let Ok(port) = port.parse::<u16>() else {
582 return Err(AttributeError::InvalidParamValue {
583 param: "Port".to_string(),
584 val: port.to_string(),
585 attr: <Self as TypedAttribute>::NAME.to_string(),
586 });
587 };
588
589 let Some(nettype) = i.next() else {
592 return Ok(Self::with_port(port));
593 };
594
595 if !is_token(nettype.as_bytes()) {
598 return Err(AttributeError::InvalidParamValue {
599 param: "Network type".to_string(),
600 val: nettype.to_string(),
601 attr: <Self as TypedAttribute>::NAME.to_string(),
602 });
603 }
604 let nettype = NetType::from_str(nettype).expect("infallible");
605
606 let Some(addrtype) = i.next() else {
607 return Err(AttributeError::ParamNotFound {
608 param: "Address type".to_string(),
609 attr: <Self as TypedAttribute>::NAME.to_string(),
610 });
611 };
612
613 if !is_token(addrtype.as_bytes()) {
614 return Err(AttributeError::InvalidParamValue {
615 param: "Address type".to_string(),
616 val: addrtype.to_string(),
617 attr: <Self as TypedAttribute>::NAME.to_string(),
618 });
619 }
620 let addrtype = AddrType::from_str(addrtype).expect("infallible");
621
622 let Some(connection_address) = i.next() else {
623 return Err(AttributeError::ParamNotFound {
624 param: "Connection address".to_string(),
625 attr: <Self as TypedAttribute>::NAME.to_string(),
626 });
627 };
628
629 if let Some(unexpected) = i.next() {
630 return Err(AttributeError::UnexpectedTrailingItem {
631 val: unexpected.to_string(),
632 attr: <Self as TypedAttribute>::NAME.to_string(),
633 });
634 }
635
636 Ok(Self {
637 port,
638 nettype: Some(nettype),
639 addrtype: Some(addrtype),
640 connection_address: Some(connection_address.to_string()),
641 })
642 }
643}
644
645impl Display for Rtcp {
646 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
647 write!(f, "{}", self.port)?;
648 if let Some(nettype) = &self.nettype {
649 write!(f, " {nettype}")?;
650 }
651 if let Some(addrtype) = &self.addrtype {
652 write!(f, " {addrtype}")?;
653 }
654 if let Some(connection_address) = &self.connection_address {
655 write!(f, " {connection_address}")?;
656 }
657 Ok(())
658 }
659}
660
661impl TypedAttribute for Rtcp {
662 const NAME: &'static str = "rtcp";
663}
664
665#[derive(Debug, PartialEq, Eq, Clone)]
669#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
670pub struct RtcpFb {
671 pub pt: RtcpFbPt,
673 pub val: RtcpFbVal,
675}
676
677impl RtcpFb {
678 pub fn new(pt: impl Into<RtcpFbPt>, val: impl Into<RtcpFbVal>) -> Self {
679 RtcpFb {
680 pt: pt.into(),
681 val: val.into(),
682 }
683 }
684}
685
686impl FromStr for RtcpFb {
687 type Err = AttributeError;
688
689 fn from_str(s: &str) -> Result<Self, Self::Err> {
690 let mut i = s.split(' ');
691 let Some(pt) = i.next() else {
692 return Err(AttributeError::ParamNotFound {
693 param: "Payload format".to_string(),
694 attr: <Self as TypedAttribute>::NAME.to_string(),
695 });
696 };
697
698 let pt = if let Ok(pt) = pt.parse::<u8>() {
699 RtcpFbPt::Fmt(pt)
700 } else if pt == "*" {
701 RtcpFbPt::Wildcard
702 } else {
703 return Err(AttributeError::InvalidParamValue {
704 param: "Payload format".to_string(),
705 val: pt.to_string(),
706 attr: <Self as TypedAttribute>::NAME.to_string(),
707 });
708 };
709
710 let Some(val) = i.next() else {
711 return Err(AttributeError::ParamNotFound {
712 param: "Rtcp feedback value".to_string(),
713 attr: <Self as TypedAttribute>::NAME.to_string(),
714 });
715 };
716
717 let rtcp_fb_val = match val {
718 "ack" => {
719 if let Some(ack_val) = i.next() {
720 let ack_val = match ack_val {
721 "rpsi" => RtcpFbAck::Rpsi,
722 "app" => {
723 if let Some(app_param) = i.next() {
724 RtcpFbAck::App(Some(app_param.to_string()))
725 } else {
726 RtcpFbAck::App(None)
727 }
728 }
729 "ccfb" => {
730 if let RtcpFbPt::Fmt(pt) = pt {
733 return Err(AttributeError::InvalidParamValue {
734 param: "Payload type of Congestion control feedback (ccfb)"
735 .to_string(),
736 val: format!("{pt}(expected wildcard (*))"),
737 attr: <Self as TypedAttribute>::NAME.to_string(),
738 });
739 } else {
740 RtcpFbAck::Ccfb
741 }
742 }
743 other => RtcpFbAck::Other(other.to_string()),
744 };
745 RtcpFbVal::Ack(Some(ack_val))
746 } else {
747 RtcpFbVal::Ack(None)
748 }
749 }
750 "nack" => {
751 if let Some(nack_val) = i.next() {
752 let nack_val = match nack_val {
753 "pli" => RtcpFbNack::Pli,
754 "sli" => RtcpFbNack::Sli,
755 "rpsi" => RtcpFbNack::Rpsi,
756 "app" => {
757 if let Some(app_param) = i.next() {
758 RtcpFbNack::App(Some(app_param.to_string()))
759 } else {
760 RtcpFbNack::App(None)
761 }
762 }
763 "ecn" => RtcpFbNack::Ecn,
764 other => RtcpFbNack::Other(other.to_string()),
765 };
766 RtcpFbVal::Nack(Some(nack_val))
767 } else {
768 RtcpFbVal::Nack(None)
769 }
770 }
771 "trr-int" => {
772 if let Some(val) = i.next() {
773 let Ok(i) = val.parse::<u64>() else {
774 return Err(AttributeError::InvalidParamValue {
775 param: "Minimum interval between RTCP packets (trr-int)".to_string(),
776 val: val.to_string(),
777 attr: <Self as TypedAttribute>::NAME.to_string(),
778 });
779 };
780 RtcpFbVal::TrrInt(i)
781 } else {
782 return Err(AttributeError::Other {
783 error: "Minimum interval between RTCP packets (trr-int) not specified"
784 .to_string(),
785 attr: <Self as TypedAttribute>::NAME.to_string(),
786 });
787 }
788 }
789 "ccm" => {
790 if let Some(ccm_val) = i.next() {
791 let ccm_val = match ccm_val {
792 "fir" => RtcpFbCcm::Fir,
793 "tmmbr" => {
794 if let Some(tmmbr_val) = i.next() {
795 RtcpFbCcm::Tmmbr(Some(tmmbr_val.to_string()))
796 } else {
797 RtcpFbCcm::Tmmbr(None)
798 }
799 }
800 "tstr" => RtcpFbCcm::Tstr,
801 "vbcm" => {
802 let mut v = vec![];
803 for vbcm_val in i {
804 let Ok(p) = vbcm_val.parse::<u8>() else {
805 return Err(AttributeError::InvalidParamValue {
806 param: "Video backchannel messages (vbcm)".to_string(),
807 val: vbcm_val.to_string(),
808 attr: <Self as TypedAttribute>::NAME.to_string(),
809 });
810 };
811 v.push(p);
812 }
813 RtcpFbCcm::Vbcm(v)
814 }
815 other => RtcpFbCcm::Other(other.to_string()),
816 };
817 RtcpFbVal::Ccm(ccm_val)
818 } else {
819 return Err(AttributeError::ParamNotFound {
820 param: "Codec control messages (ccm)".to_string(),
821 attr: <Self as TypedAttribute>::NAME.to_string(),
822 });
823 }
824 }
825 "transport-cc" => RtcpFbVal::TransportCc,
826 other => RtcpFbVal::Other(other.to_string()),
827 };
828
829 Ok(Self {
830 pt,
831 val: rtcp_fb_val,
832 })
833 }
834}
835
836impl Display for RtcpFb {
837 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
838 match self.pt {
839 RtcpFbPt::Wildcard => f.write_char('*')?,
840 RtcpFbPt::Fmt(pt) => write!(f, "{pt}")?,
841 }
842
843 f.write_char(' ')?;
844 write!(f, "{}", self.val)
845 }
846}
847
848impl TypedAttribute for RtcpFb {
849 const NAME: &'static str = "rtcp-fb";
850}
851
852#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
856#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
857pub enum Direction {
858 #[default]
859 SendRecv,
860 SendOnly,
861 RecvOnly,
862 Inactive,
863}
864
865impl Direction {
866 pub fn as_str(&self) -> &'static str {
867 match self {
868 Self::SendOnly => "sendonly",
869 Self::RecvOnly => "recvonly",
870 Self::SendRecv => "sendrecv",
871 Self::Inactive => "inactive",
872 }
873 }
874
875 pub fn has_send(self) -> bool {
876 matches!(self, Self::SendRecv | Self::SendOnly)
877 }
878
879 pub fn has_recv(self) -> bool {
880 matches!(self, Self::SendRecv | Self::RecvOnly)
881 }
882
883 pub fn reverse(self) -> Self {
884 match self {
885 Self::SendRecv => Self::SendRecv,
886 Self::SendOnly => Self::RecvOnly,
887 Self::RecvOnly => Self::SendOnly,
888 Self::Inactive => Self::Inactive,
889 }
890 }
891
892 pub fn intersect_with_remote(self, remote: Self) -> Self {
893 match (self, remote) {
894 (Self::Inactive, _)
895 | (_, Self::Inactive)
896 | (Self::RecvOnly, Self::RecvOnly)
897 | (Self::SendOnly, Self::SendOnly) => Self::Inactive,
898 (Self::SendRecv, Self::SendRecv) => Self::SendRecv,
899 (Self::SendOnly, Self::RecvOnly | Self::SendRecv)
900 | (Self::SendRecv, Self::RecvOnly) => Self::SendOnly,
901 (Self::RecvOnly, Self::SendRecv | Self::SendOnly)
902 | (Self::SendRecv, Self::SendOnly) => Self::RecvOnly,
903 }
904 }
905}
906
907impl FromStr for Direction {
908 type Err = ParseEnumError;
909
910 fn from_str(s: &str) -> Result<Self, Self::Err> {
911 if "sendonly".eq_ignore_ascii_case(s) {
912 Ok(Direction::SendOnly)
913 } else if "recvonly".eq_ignore_ascii_case(s) {
914 Ok(Direction::RecvOnly)
915 } else if "sendrecv".eq_ignore_ascii_case(s) {
916 Ok(Direction::SendRecv)
917 } else if "inactive".eq_ignore_ascii_case(s) {
918 Ok(Direction::Inactive)
919 } else {
920 Err(ParseEnumError::Invalid(s.to_string()))
921 }
922 }
923}
924
925impl Display for Direction {
926 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
927 f.write_str(self.as_str())
928 }
929}
930
931impl From<Direction> for Attribute {
932 fn from(attr: Direction) -> Attribute {
933 Attribute {
934 attribute: attr.to_string(),
935 value: None,
936 }
937 }
938}
939
940#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
944#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
945pub struct ExtMap {
946 pub id: u8,
948 pub direction: Option<Direction>,
950 pub uri: String,
952 pub attributes: Option<String>,
954}
955
956impl ExtMap {
957 pub fn new(id: u8, uri: impl ToString) -> Self {
958 ExtMap {
959 id,
960 direction: None,
961 uri: uri.to_string(),
962 attributes: None,
963 }
964 }
965
966 pub fn builder(id: u8, uri: impl ToString) -> builders::ExtMap {
967 builders::ExtMap::new(id, uri)
968 }
969
970 pub fn with_direction(id: u8, direction: Direction, uri: impl ToString) -> Self {
971 ExtMap {
972 id,
973 direction: Some(direction),
974 uri: uri.to_string(),
975 attributes: None,
976 }
977 }
978
979 pub fn with_direction_and_attributes(
980 id: u8,
981 direction: Direction,
982 uri: impl ToString,
983 attributes: impl ToString,
984 ) -> Self {
985 ExtMap {
986 id,
987 direction: Some(direction),
988 uri: uri.to_string(),
989 attributes: Some(attributes.to_string()),
990 }
991 }
992
993 pub fn set_direction(&mut self, direction: Direction) {
994 self.direction = Some(direction);
995 }
996
997 pub fn set_attributes(&mut self, attributes: impl ToString) {
998 self.attributes = Some(attributes.to_string());
999 }
1000}
1001
1002impl FromStr for ExtMap {
1003 type Err = AttributeError;
1004
1005 fn from_str(s: &str) -> Result<Self, Self::Err> {
1006 let mut i = s.splitn(3, ' ');
1007
1008 let Some(id_direction) = i.next() else {
1009 return Err(AttributeError::ParamNotFound {
1010 param: "id/direction".to_string(),
1011 attr: <Self as TypedAttribute>::NAME.to_string(),
1012 });
1013 };
1014
1015 let mut d = id_direction.split('/');
1016
1017 let Some(id) = d.next() else {
1018 return Err(AttributeError::ParamNotFound {
1019 param: "id".to_string(),
1020 attr: <Self as TypedAttribute>::NAME.to_string(),
1021 });
1022 };
1023
1024 let direction = if let Some(d) = d.next() {
1025 let Ok(dir) = Direction::from_str(d) else {
1026 return Err(AttributeError::InvalidParamValue {
1027 param: "Direction".to_string(),
1028 val: d.to_string(),
1029 attr: <Self as TypedAttribute>::NAME.to_string(),
1030 });
1031 };
1032 Some(dir)
1033 } else {
1034 None
1035 };
1036
1037 let Ok(id) = id.parse::<u8>() else {
1038 return Err(AttributeError::InvalidParamValue {
1039 param: "Id".to_string(),
1040 val: id.to_string(),
1041 attr: <Self as TypedAttribute>::NAME.to_string(),
1042 });
1043 };
1044
1045 if id == 0 {
1046 return Err(AttributeError::InvalidParamValue {
1047 param: "Id".to_string(),
1048 val: id.to_string(),
1049 attr: <Self as TypedAttribute>::NAME.to_string(),
1050 });
1051 }
1052
1053 let Some(uri) = i.next() else {
1054 return Err(AttributeError::ParamNotFound {
1055 param: "URI".to_string(),
1056 attr: <Self as TypedAttribute>::NAME.to_string(),
1057 });
1058 };
1059
1060 let attributes = i.next().map(|attr| attr.to_string());
1061
1062 Ok(Self {
1063 id,
1064 direction,
1065 uri: uri.to_string(),
1066 attributes,
1067 })
1068 }
1069}
1070
1071impl Display for ExtMap {
1072 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1073 write!(f, "{}", self.id)?;
1074 if let Some(direction) = &self.direction {
1075 f.write_char('/')?;
1076 f.write_str(direction.as_str())?;
1077 }
1078
1079 f.write_char(' ')?;
1080 f.write_str(&self.uri)?;
1081
1082 if let Some(attr) = &self.attributes {
1083 f.write_char(' ')?;
1084 f.write_str(attr.as_str())?;
1085 }
1086 Ok(())
1087 }
1088}
1089
1090impl TypedAttribute for ExtMap {
1091 const NAME: &'static str = "extmap";
1092}
1093
1094#[derive(Debug, Clone, PartialEq, Eq)]
1098#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1099pub struct Fingerprint {
1100 pub hash_func: HashFunc,
1102 pub fingerprint: Vec<u8>,
1104}
1105
1106impl Fingerprint {
1107 pub fn new(hash_func: HashFunc) -> Self {
1108 Fingerprint {
1109 hash_func,
1110 fingerprint: vec![],
1111 }
1112 }
1113
1114 pub fn with_fingerprint(
1115 hash_func: HashFunc,
1116 fingerprint: impl IntoIterator<Item = u8>,
1117 ) -> Self {
1118 Fingerprint {
1119 hash_func,
1120 fingerprint: std::iter::FromIterator::from_iter(fingerprint),
1121 }
1122 }
1123
1124 pub fn set_fingerprint(&mut self, fingerprint: impl IntoIterator<Item = u8>) {
1125 self.fingerprint.clear();
1126 self.fingerprint.extend(fingerprint);
1127 }
1128}
1129
1130impl FromStr for Fingerprint {
1131 type Err = AttributeError;
1132
1133 fn from_str(s: &str) -> Result<Self, Self::Err> {
1134 let mut i = s.splitn(2, ' ');
1135
1136 let hash_func = if let Some(hash_func) = i.next() {
1137 HashFunc::new(hash_func)
1138 } else {
1139 return Err(AttributeError::ParamNotFound {
1140 param: "Hash function".to_string(),
1141 attr: <Self as TypedAttribute>::NAME.to_string(),
1142 });
1143 };
1144
1145 let mut fingerprint: Vec<u8> = vec![];
1146 if let Some(fp) = i.next() {
1147 for f in fp.split(':') {
1148 let Ok(mut f) = hex::decode(f) else {
1149 return Err(AttributeError::InvalidParamValue {
1150 param: "Fingerprint value".to_string(),
1151 val: f.to_string(),
1152 attr: <Self as TypedAttribute>::NAME.to_string(),
1153 });
1154 };
1155
1156 fingerprint.append(&mut f);
1157 }
1158 } else {
1159 return Err(AttributeError::ParamNotFound {
1160 param: "Hash value".to_string(),
1161 attr: <Self as TypedAttribute>::NAME.to_string(),
1162 });
1163 };
1164
1165 Ok(Self {
1166 hash_func,
1167 fingerprint,
1168 })
1169 }
1170}
1171
1172impl Display for Fingerprint {
1173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1174 f.write_str(self.hash_func.as_str())?;
1175 let mut first = true;
1176 for v in &self.fingerprint {
1177 if first {
1178 f.write_char(' ')?;
1179 first = false;
1180 } else {
1181 f.write_char(':')?;
1182 }
1183 write!(f, "{v:02X}")?;
1184 }
1185 Ok(())
1186 }
1187}
1188
1189impl TypedAttribute for Fingerprint {
1190 const NAME: &'static str = "fingerprint";
1191}
1192
1193#[derive(Debug, Clone, PartialEq, Eq)]
1197#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1198pub struct Group {
1199 pub semantics: GroupSemantics,
1200 pub mid_tags: Vec<String>,
1201}
1202
1203impl Group {
1204 pub fn new(semantics: GroupSemantics) -> Self {
1205 Group {
1206 semantics,
1207 mid_tags: vec![],
1208 }
1209 }
1210
1211 pub fn add_mid_tag(&mut self, mid_tag: impl ToString) {
1212 self.mid_tags.push(mid_tag.to_string())
1213 }
1214
1215 pub fn add_mid_tags(&mut self, mid_tags: impl IntoIterator<Item = impl ToString>) {
1216 self.mid_tags
1217 .extend(mid_tags.into_iter().map(|i| i.to_string()))
1218 }
1219}
1220
1221impl FromStr for Group {
1222 type Err = AttributeError;
1223
1224 fn from_str(s: &str) -> Result<Self, Self::Err> {
1225 let mut i = s.split(' ');
1226
1227 let Some(semantics) = i.next() else {
1228 return Err(AttributeError::ParamNotFound {
1229 param: "Semantics".to_string(),
1230 attr: <Self as TypedAttribute>::NAME.to_string(),
1231 });
1232 };
1233
1234 let semantics = GroupSemantics::new(semantics);
1235
1236 let mut mid_tags = vec![];
1237 for mid in i {
1238 mid_tags.push(mid.to_string());
1239 }
1240
1241 if mid_tags.is_empty() {
1242 return Err(AttributeError::ParamNotFound {
1243 param: "Media identification tags".to_string(),
1244 attr: <Self as TypedAttribute>::NAME.to_string(),
1245 });
1246 }
1247
1248 Ok(Self {
1249 semantics,
1250 mid_tags,
1251 })
1252 }
1253}
1254
1255impl Display for Group {
1256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1257 f.write_str(self.semantics.as_str())?;
1258 for m in &self.mid_tags {
1259 f.write_char(' ')?;
1260 f.write_str(m)?;
1261 }
1262 Ok(())
1263 }
1264}
1265
1266impl TypedAttribute for Group {
1267 const NAME: &'static str = "group";
1268}
1269
1270#[derive(Debug, Clone, PartialEq, Eq)]
1274#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1275pub enum Setup {
1276 Active,
1278 Passive,
1280 ActPass,
1282 HoldConn,
1284}
1285
1286impl FromStr for Setup {
1287 type Err = AttributeError;
1288
1289 fn from_str(s: &str) -> Result<Self, Self::Err> {
1290 if "active".eq_ignore_ascii_case(s) {
1291 Ok(Setup::Active)
1292 } else if "passive".eq_ignore_ascii_case(s) {
1293 Ok(Setup::Passive)
1294 } else if "actpass".eq_ignore_ascii_case(s) {
1295 Ok(Setup::ActPass)
1296 } else if "holdconn".eq_ignore_ascii_case(s) {
1297 Ok(Setup::HoldConn)
1298 } else {
1299 Err(AttributeError::Other {
1300 error: format!("Invalid Setup value {s}"),
1301 attr: <Self as TypedAttribute>::NAME.to_string(),
1302 })
1303 }
1304 }
1305}
1306
1307impl Display for Setup {
1308 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1309 let s = match self {
1310 Setup::Active => "active",
1311 Setup::Passive => "passive",
1312 Setup::ActPass => "actpass",
1313 Setup::HoldConn => "holdconn",
1314 };
1315 f.write_str(s)
1316 }
1317}
1318
1319impl TypedAttribute for Setup {
1320 const NAME: &'static str = "setup";
1321}
1322
1323#[derive(Debug, Clone, PartialEq, Eq)]
1327#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1328pub struct Ssrc {
1329 pub ssrc_id: u32,
1330 pub attribute: SsrcAttribute,
1331 pub value: Option<String>,
1332}
1333
1334impl Ssrc {
1335 pub fn new(ssrc_id: u32, attribute: SsrcAttribute) -> Self {
1336 Ssrc {
1337 ssrc_id,
1338 attribute,
1339 value: None,
1340 }
1341 }
1342
1343 pub fn with_typed_attribute(ssrc_id: u32, attribute: impl TypedAttribute) -> Self {
1344 let value = attribute.to_string();
1345 Ssrc {
1346 ssrc_id,
1347 attribute: SsrcAttribute::from(attribute),
1348 value: Some(value),
1349 }
1350 }
1351
1352 pub fn with_value(ssrc_id: u32, attribute: SsrcAttribute, value: impl ToString) -> Self {
1353 Ssrc {
1354 ssrc_id,
1355 attribute,
1356 value: Some(value.to_string()),
1357 }
1358 }
1359
1360 pub fn set_value(&mut self, value: impl ToString) {
1361 self.value = Some(value.to_string());
1362 }
1363
1364 pub fn get_typed<T: TypedAttribute>(&self) -> Result<T, AttributeError> {
1372 if !self.attribute.as_str().eq_ignore_ascii_case(T::NAME) {
1373 return Err(AttributeError::Other {
1374 error: format!("Attribute type mismatch (requested {})", T::NAME),
1375 attr: self.attribute.as_str().to_string(),
1376 });
1377 }
1378
1379 T::from_str(self.value.as_deref().unwrap_or_default())
1382 }
1383}
1384
1385impl FromStr for Ssrc {
1386 type Err = AttributeError;
1387
1388 fn from_str(s: &str) -> Result<Self, Self::Err> {
1389 let Some((ssrc_id_str, rest)) = s.split_once(' ') else {
1390 return Err(AttributeError::ParamNotFound {
1391 param: "Ssrc id".to_string(),
1392 attr: <Self as TypedAttribute>::NAME.to_string(),
1393 });
1394 };
1395
1396 let Ok(ssrc_id) = ssrc_id_str.parse::<u32>() else {
1397 return Err(AttributeError::InvalidParamValue {
1398 param: "Ssrc id".to_string(),
1399 val: ssrc_id_str.to_string(),
1400 attr: <Self as TypedAttribute>::NAME.to_string(),
1401 });
1402 };
1403
1404 let (attr, value) = if let Some((attr_str, value)) = rest.split_once(':') {
1405 (attr_str, Some(value.to_string()))
1406 } else {
1407 (rest, None)
1408 };
1409
1410 Ok(Self {
1411 ssrc_id,
1412 attribute: SsrcAttribute::new(attr),
1413 value,
1414 })
1415 }
1416}
1417
1418impl Display for Ssrc {
1419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1420 use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
1421
1422 let attr_str = match &self.attribute {
1423 SsrcAttribute::Cname => "cname",
1424 SsrcAttribute::PreviousSsrc => "previous-ssrc",
1425 SsrcAttribute::Fmtp => <Fmtp as TypedAttribute>::NAME,
1426 SsrcAttribute::Rtcp => <Rtcp as TypedAttribute>::NAME,
1427 SsrcAttribute::ReferenceClock => <ReferenceClock as TypedAttribute>::NAME,
1428 SsrcAttribute::MediaClockSource => <MediaClockSource as TypedAttribute>::NAME,
1429 SsrcAttribute::Other(other) => other.as_str(),
1430 };
1431 write!(f, "{} {attr_str}", self.ssrc_id)?;
1432
1433 if let Some(value) = &self.value {
1434 f.write_char(':')?;
1435 f.write_str(value)?;
1436 }
1437
1438 Ok(())
1439 }
1440}
1441
1442impl TypedAttribute for Ssrc {
1443 const NAME: &'static str = "ssrc";
1444}
1445
1446#[derive(Debug, Clone, PartialEq, Eq)]
1450#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1451pub struct SsrcGroup {
1452 pub semantics: GroupSemantics,
1453 pub ssrc_ids: Vec<u32>,
1454}
1455
1456impl SsrcGroup {
1457 pub fn new(semantics: GroupSemantics) -> Self {
1458 SsrcGroup {
1459 semantics,
1460 ssrc_ids: vec![],
1461 }
1462 }
1463
1464 pub fn add_ssrc_id(&mut self, ssrc_id: u32) {
1465 self.ssrc_ids.push(ssrc_id)
1466 }
1467
1468 pub fn add_ssrc_ids(&mut self, ssrc_ids: impl IntoIterator<Item = u32>) {
1469 self.ssrc_ids.extend(ssrc_ids)
1470 }
1471}
1472
1473impl FromStr for SsrcGroup {
1474 type Err = AttributeError;
1475
1476 fn from_str(s: &str) -> Result<Self, Self::Err> {
1477 let mut i = s.split(' ');
1478
1479 let Some(semantics) = i.next() else {
1480 return Err(AttributeError::ParamNotFound {
1481 param: "Semantics".to_string(),
1482 attr: <Self as TypedAttribute>::NAME.to_string(),
1483 });
1484 };
1485
1486 let semantics = if "FEC".eq_ignore_ascii_case(semantics) {
1487 GroupSemantics::FEC
1488 } else if "FID".eq_ignore_ascii_case(semantics) {
1489 GroupSemantics::FID
1490 } else {
1491 GroupSemantics::Other(semantics.to_string())
1495 };
1496
1497 let mut ssrc_ids = vec![];
1498 for ssrc_id in i {
1499 let Ok(ssrc_id) = ssrc_id.parse::<u32>() else {
1500 return Err(AttributeError::InvalidParamValue {
1501 param: "Ssrc id".to_string(),
1502 val: ssrc_id.to_string(),
1503 attr: <Self as TypedAttribute>::NAME.to_string(),
1504 });
1505 };
1506 ssrc_ids.push(ssrc_id);
1507 }
1508
1509 if ssrc_ids.is_empty() {
1510 return Err(AttributeError::ParamNotFound {
1511 param: "ssrc_id".to_string(),
1512 attr: <Self as TypedAttribute>::NAME.to_string(),
1513 });
1514 }
1515
1516 Ok(Self {
1517 semantics,
1518 ssrc_ids,
1519 })
1520 }
1521}
1522
1523impl Display for SsrcGroup {
1524 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1525 let sem = match &self.semantics {
1526 GroupSemantics::FEC => "FEC",
1527 GroupSemantics::FID => "FID",
1528 GroupSemantics::LS => "LS",
1531 GroupSemantics::SRF => "SRF",
1532 GroupSemantics::ANAT => "ANAT",
1533 GroupSemantics::DDP => "DDP",
1534 GroupSemantics::Other(s) => s.as_str(),
1535 };
1536
1537 f.write_str(sem)?;
1538 for ssrc_id in &self.ssrc_ids {
1539 f.write_char(' ')?;
1540 write!(f, "{ssrc_id}")?;
1541 }
1542
1543 Ok(())
1544 }
1545}
1546
1547impl TypedAttribute for SsrcGroup {
1548 const NAME: &'static str = "ssrc-group";
1549}
1550
1551#[derive(Debug, PartialEq, Eq, Clone)]
1555#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1556pub struct SrtpKeyParam {
1557 pub key_and_salt: String,
1559 pub lifetime: Option<u64>,
1563 pub mki_and_length: Option<(u32, u32)>,
1565}
1566
1567impl SrtpKeyParam {
1568 pub fn new(key_and_salt: impl ToString) -> Self {
1569 SrtpKeyParam {
1570 key_and_salt: key_and_salt.to_string(),
1571 lifetime: None,
1572 mki_and_length: None,
1573 }
1574 }
1575
1576 pub fn with_lifetime(key_and_salt: impl ToString, lifetime: u64) -> Self {
1577 SrtpKeyParam {
1578 key_and_salt: key_and_salt.to_string(),
1579 lifetime: Some(lifetime),
1580 mki_and_length: None,
1581 }
1582 }
1583
1584 pub fn with_lifetime_and_mki_and_length(
1585 key_and_salt: impl ToString,
1586 lifetime: u64,
1587 mki: u32,
1588 length: u32,
1589 ) -> Self {
1590 SrtpKeyParam {
1591 key_and_salt: key_and_salt.to_string(),
1592 lifetime: Some(lifetime),
1593 mki_and_length: Some((mki, length)),
1594 }
1595 }
1596
1597 pub fn set_lifetime(&mut self, lifetime: u64) {
1598 self.lifetime = Some(lifetime);
1599 }
1600
1601 pub fn set_mki_and_length(&mut self, mki: u32, length: u32) {
1602 self.mki_and_length = Some((mki, length));
1603 }
1604}
1605
1606fn parse_decimal_no_leading_zeroes(s: &str) -> Option<u64> {
1610 if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
1611 return None;
1612 }
1613 if s.len() > 1 && s.starts_with('0') {
1614 return None;
1615 }
1616 s.parse().ok()
1617}
1618
1619impl FromStr for SrtpKeyParam {
1620 type Err = AttributeError;
1621 fn from_str(key_param: &str) -> Result<Self, Self::Err> {
1622 let mut k = key_param.split('|');
1623
1624 let Some(key_and_salt_with_method) = k.next() else {
1625 return Err(AttributeError::ParamNotFound {
1626 param: "Srtp Key and Salt".to_string(),
1627 attr: Crypto::NAME.to_string(),
1628 });
1629 };
1630
1631 let key_and_salt = if key_and_salt_with_method
1632 .get(..7)
1633 .is_some_and(|p| p.eq_ignore_ascii_case("inline:"))
1634 {
1635 &key_and_salt_with_method[7..]
1636 } else {
1637 return Err(AttributeError::InvalidParamValue {
1638 param: "Srtp Key and Salt".to_string(),
1639 val: key_and_salt_with_method.to_string(),
1640 attr: Crypto::NAME.to_string(),
1641 });
1642 };
1643
1644 let (lifetime, mki_and_length) = if let Some(next_param) = k.next() {
1645 match next_param.split_once(':') {
1646 Some(mki_and_length) => {
1647 let Ok(mki) = mki_and_length.0.parse::<u32>() else {
1649 return Err(AttributeError::InvalidParamValue {
1650 param: "MKI".to_string(),
1651 val: next_param.to_string(),
1652 attr: Crypto::NAME.to_string(),
1653 });
1654 };
1655
1656 let Ok(len) = mki_and_length.1.parse::<u32>() else {
1657 return Err(AttributeError::InvalidParamValue {
1658 param: "Length".to_string(),
1659 val: next_param.to_string(),
1660 attr: Crypto::NAME.to_string(),
1661 });
1662 };
1663 (None, Some((mki, len)))
1664 }
1665 None => {
1666 const MAX_LIFETIME: u64 = 1 << 48;
1670 let lifetime = match next_param.strip_prefix("2^") {
1671 Some(exp) => {
1672 let Some(exp) = parse_decimal_no_leading_zeroes(exp)
1673 .and_then(|exp| u32::try_from(exp).ok())
1674 else {
1675 return Err(AttributeError::InvalidParamValue {
1676 param: "Lifetime".to_string(),
1677 val: next_param.to_string(),
1678 attr: Crypto::NAME.to_string(),
1679 });
1680 };
1681 let Some(lifetime) = 1u64.checked_shl(exp) else {
1682 return Err(AttributeError::InvalidParamValue {
1683 param: "Lifetime".to_string(),
1684 val: next_param.to_string(),
1685 attr: Crypto::NAME.to_string(),
1686 });
1687 };
1688 if lifetime > MAX_LIFETIME {
1690 return Err(AttributeError::InvalidParamValue {
1691 param: "Lifetime".to_string(),
1692 val: next_param.to_string(),
1693 attr: Crypto::NAME.to_string(),
1694 });
1695 }
1696 Some(lifetime)
1697 }
1698 None => {
1699 let Some(lifetime) = parse_decimal_no_leading_zeroes(next_param) else {
1700 return Err(AttributeError::InvalidParamValue {
1701 param: "Lifetime".to_string(),
1702 val: next_param.to_string(),
1703 attr: Crypto::NAME.to_string(),
1704 });
1705 };
1706 if lifetime == 0 || lifetime > MAX_LIFETIME {
1707 return Err(AttributeError::InvalidParamValue {
1708 param: "Lifetime".to_string(),
1709 val: next_param.to_string(),
1710 attr: Crypto::NAME.to_string(),
1711 });
1712 }
1713 Some(lifetime)
1714 }
1715 };
1716
1717 let mki_and_length = if let Some(m) = k.next() {
1719 if let Some(p) = m.split_once(':') {
1720 let Ok(mki) = p.0.parse::<u32>() else {
1721 return Err(AttributeError::InvalidParamValue {
1722 param: "MKI".to_string(),
1723 val: m.to_string(),
1724 attr: Crypto::NAME.to_string(),
1725 });
1726 };
1727
1728 let Ok(len) = p.1.parse::<u32>() else {
1729 return Err(AttributeError::InvalidParamValue {
1730 param: "Length".to_string(),
1731 val: m.to_string(),
1732 attr: Crypto::NAME.to_string(),
1733 });
1734 };
1735 Some((mki, len))
1736 } else {
1737 return Err(AttributeError::ParamNotFound {
1738 param: "MKI and Length".to_string(),
1739 attr: Crypto::NAME.to_string(),
1740 });
1741 }
1742 } else {
1743 None
1744 };
1745
1746 (lifetime, mki_and_length)
1747 }
1748 }
1749 } else {
1750 (None, None)
1751 };
1752
1753 if let Some((_, len)) = mki_and_length {
1754 if !(1..=128).contains(&len) {
1755 return Err(AttributeError::InvalidParamValue {
1756 param: "MKI length".to_string(),
1757 val: len.to_string(),
1758 attr: Crypto::NAME.to_string(),
1759 });
1760 }
1761 }
1762
1763 if let Some(unexpected) = k.next() {
1764 return Err(AttributeError::UnexpectedTrailingItem {
1765 val: unexpected.to_string(),
1766 attr: Crypto::NAME.to_string(),
1767 });
1768 }
1769
1770 Ok(Self {
1771 key_and_salt: key_and_salt.to_string(),
1772 lifetime,
1773 mki_and_length,
1774 })
1775 }
1776}
1777
1778impl Display for SrtpKeyParam {
1779 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1780 write!(f, "inline:{}", self.key_and_salt)?;
1781 if let Some(lifetime) = self.lifetime {
1782 if lifetime.is_power_of_two() {
1783 write!(f, "|2^{}", lifetime.trailing_zeros())?;
1784 } else {
1785 write!(f, "|{lifetime}")?;
1786 }
1787 }
1788 if let Some((mki, length)) = self.mki_and_length {
1789 write!(f, "|{mki}:{length}")?;
1790 }
1791 Ok(())
1792 }
1793}
1794
1795#[derive(Debug, PartialEq, Eq, Clone)]
1799#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1800pub struct Crypto {
1801 pub tag: u32,
1802 pub crypto_suite: CryptoSuite,
1803 pub key_params: Vec<SrtpKeyParam>,
1804 pub session_params: Vec<SrtpSessionParam>,
1805}
1806
1807impl Crypto {
1808 pub fn new(tag: u32, crypto_suite: CryptoSuite) -> Self {
1809 Crypto {
1810 tag,
1811 crypto_suite,
1812 key_params: vec![],
1813 session_params: vec![],
1814 }
1815 }
1816
1817 pub fn add_key_param(&mut self, key_param: SrtpKeyParam) {
1818 self.key_params.push(key_param)
1819 }
1820
1821 pub fn add_key_params(&mut self, key_params: impl IntoIterator<Item = SrtpKeyParam>) {
1822 self.key_params.extend(key_params)
1823 }
1824
1825 pub fn add_session_param(&mut self, session_param: SrtpSessionParam) {
1826 self.session_params.push(session_param)
1827 }
1828
1829 pub fn add_session_params(
1830 &mut self,
1831 session_params: impl IntoIterator<Item = SrtpSessionParam>,
1832 ) {
1833 self.session_params.extend(session_params)
1834 }
1835}
1836
1837impl FromStr for Crypto {
1838 type Err = AttributeError;
1839
1840 fn from_str(s: &str) -> Result<Self, Self::Err> {
1841 let mut i = s.split(' ');
1842
1843 let Some(tag) = i.next() else {
1844 return Err(AttributeError::ParamNotFound {
1845 param: "Tag".to_string(),
1846 attr: <Self as TypedAttribute>::NAME.to_string(),
1847 });
1848 };
1849
1850 let Ok(tag) = tag.parse::<u32>() else {
1851 return Err(AttributeError::InvalidParamValue {
1852 param: "Tag".to_string(),
1853 val: tag.to_string(),
1854 attr: <Self as TypedAttribute>::NAME.to_string(),
1855 });
1856 };
1857
1858 let Some(crypto_suite) = i.next() else {
1859 return Err(AttributeError::ParamNotFound {
1860 param: "CryptoSuite".to_string(),
1861 attr: <Self as TypedAttribute>::NAME.to_string(),
1862 });
1863 };
1864
1865 let crypto_suite = CryptoSuite::new(crypto_suite);
1866
1867 let Some(key_params_str) = i.next() else {
1868 return Err(AttributeError::ParamNotFound {
1869 param: "Key params".to_string(),
1870 attr: <Self as TypedAttribute>::NAME.to_string(),
1871 });
1872 };
1873
1874 let mut key_params: Vec<SrtpKeyParam> = Vec::new();
1875
1876 for key_param in key_params_str.split(';') {
1877 let key_param = SrtpKeyParam::from_str(key_param)?;
1878 key_params.push(key_param);
1879 }
1880
1881 let mut session_params: Vec<SrtpSessionParam> = Vec::new();
1882 for s in &mut i {
1883 let param = if s.get(..4).is_some_and(|p| p.eq_ignore_ascii_case("KDR=")) {
1884 let kdr_val = &s[4..];
1885 let Ok(kdr_val) = kdr_val.parse::<u8>() else {
1886 return Err(AttributeError::InvalidParamValue {
1887 param: "KDR".to_string(),
1888 val: kdr_val.to_string(),
1889 attr: <Self as TypedAttribute>::NAME.to_string(),
1890 });
1891 };
1892
1893 if !(0..=24).contains(&kdr_val) {
1898 return Err(AttributeError::InvalidParamValue {
1899 param: "KDR".to_string(),
1900 val: format!("{kdr_val}(expected range 0..24)"),
1901 attr: <Self as TypedAttribute>::NAME.to_string(),
1902 });
1903 }
1904 SrtpSessionParam::Kdr(kdr_val)
1905 } else if s.eq_ignore_ascii_case("UNENCRYPTED_SRTCP") {
1906 SrtpSessionParam::UnencryptedSrtcp
1907 } else if s.eq_ignore_ascii_case("UNENCRYPTED_SRTP") {
1908 SrtpSessionParam::UnencryptedSrtp
1909 } else if s.eq_ignore_ascii_case("UNAUTHENTICATED_SRTP") {
1910 SrtpSessionParam::UnauthenticatedSrtp
1911 } else if s
1912 .get(..10)
1913 .is_some_and(|p| p.eq_ignore_ascii_case("FEC_ORDER="))
1914 {
1915 let fec_ord = &s[10..];
1916 if fec_ord.eq_ignore_ascii_case("FEC_SRTP") {
1917 SrtpSessionParam::FecOrder(FecOrder::FecSrtp)
1918 } else if fec_ord.eq_ignore_ascii_case("SRTP_FEC") {
1919 SrtpSessionParam::FecOrder(FecOrder::SrtpFec)
1920 } else {
1921 return Err(AttributeError::InvalidParamValue {
1922 param: "FEC order".to_string(),
1923 val: s.to_string(),
1924 attr: <Self as TypedAttribute>::NAME.to_string(),
1925 });
1926 }
1927 } else if s
1928 .get(..8)
1929 .is_some_and(|p| p.eq_ignore_ascii_case("FEC_KEY="))
1930 {
1931 let key_params_str = &s[8..];
1932 let mut key_params: Vec<SrtpKeyParam> = Vec::new();
1933
1934 for key_param in key_params_str.split(';') {
1935 let key_param = SrtpKeyParam::from_str(key_param)?;
1936 key_params.push(key_param);
1937 }
1938 SrtpSessionParam::FecKey(key_params)
1939 } else if s.get(..4).is_some_and(|p| p.eq_ignore_ascii_case("WSH=")) {
1940 let wsh_val = &s[4..];
1941 let Ok(wsh_val) = wsh_val.parse::<u8>() else {
1942 return Err(AttributeError::InvalidParamValue {
1943 param: "WSH".to_string(),
1944 val: wsh_val.to_string(),
1945 attr: <Self as TypedAttribute>::NAME.to_string(),
1946 });
1947 };
1948
1949 if wsh_val < 64 {
1950 return Err(AttributeError::InvalidParamValue {
1951 param: "WSH".to_string(),
1952 val: wsh_val.to_string(),
1953 attr: <Self as TypedAttribute>::NAME.to_string(),
1954 });
1955 }
1956 SrtpSessionParam::Wsh(wsh_val)
1957 } else {
1958 SrtpSessionParam::Extension(s.to_string())
1960 };
1961 session_params.push(param);
1962 }
1963
1964 Ok(Self {
1965 tag,
1966 key_params,
1967 crypto_suite,
1968 session_params,
1969 })
1970 }
1971}
1972
1973impl Display for Crypto {
1974 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1975 write!(f, "{} {}", self.tag, self.crypto_suite.as_str())?;
1976
1977 for (i, key_param) in self.key_params.iter().enumerate() {
1978 if i == 0 {
1979 f.write_char(' ')?;
1980 } else {
1981 f.write_char(';')?;
1982 }
1983
1984 write!(f, "{}", key_param)?;
1985 }
1986
1987 for session_param in &self.session_params {
1988 match session_param {
1989 SrtpSessionParam::Kdr(kdr) => write!(f, " KDR={kdr}")?,
1990 SrtpSessionParam::UnencryptedSrtp => write!(f, " UNENCRYPTED_SRTP")?,
1991 SrtpSessionParam::UnencryptedSrtcp => write!(f, " UNENCRYPTED_SRTCP")?,
1992 SrtpSessionParam::UnauthenticatedSrtp => write!(f, " UNAUTHENTICATED_SRTP")?,
1993 SrtpSessionParam::FecOrder(fec_order) => {
1994 let order = match fec_order {
1995 FecOrder::FecSrtp => "FEC_SRTP",
1996 FecOrder::SrtpFec => "SRTP_FEC",
1997 };
1998 write!(f, " FEC_ORDER={order}")?;
1999 }
2000 SrtpSessionParam::FecKey(srtp_key_params) => {
2001 write!(f, " FEC_KEY")?;
2002 for (i, key_param) in srtp_key_params.iter().enumerate() {
2003 if i == 0 {
2004 f.write_char('=')?;
2005 } else {
2006 f.write_char(';')?;
2007 }
2008
2009 write!(f, "{}", key_param)?;
2010 }
2011 }
2012 SrtpSessionParam::Wsh(wsh) => write!(f, " WSH={wsh}")?,
2013 SrtpSessionParam::Extension(extn) => {
2014 f.write_char(' ')?;
2015 f.write_str(extn)?;
2016 }
2017 }
2018 }
2019 Ok(())
2020 }
2021}
2022
2023impl TypedAttribute for Crypto {
2024 const NAME: &'static str = "crypto";
2025}
2026
2027#[derive(Debug, PartialEq, Eq, Clone)]
2031#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2032pub struct Candidate {
2033 pub foundation: String,
2036 pub component_id: u32,
2039 pub transport: String,
2041 pub priority: u64,
2043 pub address: CandidateAddress,
2046 pub port: u16,
2048 pub typ: CandidateType,
2050 pub rel_addr: Option<IpAddr>,
2053 pub rel_port: Option<u16>,
2056 pub extensions: Vec<(String, String)>,
2058}
2059
2060impl Candidate {
2061 pub fn new(
2062 foundation: impl ToString,
2063 component_id: u32,
2064 transport: impl ToString,
2065 priority: u64,
2066 address: CandidateAddress,
2067 port: u16,
2068 typ: CandidateType,
2069 ) -> Self {
2070 Candidate {
2071 foundation: foundation.to_string(),
2072 component_id,
2073 transport: transport.to_string(),
2074 priority,
2075 address,
2076 port,
2077 typ,
2078 rel_addr: None,
2079 rel_port: None,
2080 extensions: vec![],
2081 }
2082 }
2083
2084 #[allow(clippy::too_many_arguments)]
2085 pub fn with_rel_addr(
2086 foundation: impl ToString,
2087 component_id: u32,
2088 transport: impl ToString,
2089 priority: u64,
2090 address: CandidateAddress,
2091 port: u16,
2092 typ: CandidateType,
2093 rel_addr: impl Into<IpAddr>,
2094 ) -> Self {
2095 Candidate {
2096 foundation: foundation.to_string(),
2097 component_id,
2098 transport: transport.to_string(),
2099 priority,
2100 address,
2101 port,
2102 typ,
2103 rel_addr: Some(rel_addr.into()),
2104 rel_port: None,
2105 extensions: vec![],
2106 }
2107 }
2108
2109 #[allow(clippy::too_many_arguments)]
2110 pub fn with_rel_addr_and_port(
2111 foundation: impl ToString,
2112 component_id: u32,
2113 transport: impl ToString,
2114 priority: u64,
2115 address: CandidateAddress,
2116 port: u16,
2117 typ: CandidateType,
2118 rel_addr: impl Into<IpAddr>,
2119 rel_port: u16,
2120 ) -> Self {
2121 Candidate {
2122 foundation: foundation.to_string(),
2123 component_id,
2124 transport: transport.to_string(),
2125 priority,
2126 address,
2127 port,
2128 typ,
2129 rel_addr: Some(rel_addr.into()),
2130 rel_port: Some(rel_port),
2131 extensions: vec![],
2132 }
2133 }
2134
2135 pub fn set_rel_addr(&mut self, rel_addr: impl Into<IpAddr>) {
2136 self.rel_addr = Some(rel_addr.into());
2137 }
2138
2139 pub fn set_rel_port(&mut self, rel_port: u16) {
2140 self.rel_port = Some(rel_port);
2141 }
2142
2143 pub fn add_extension(&mut self, name: impl ToString, value: impl ToString) {
2144 self.extensions.push((name.to_string(), value.to_string()))
2145 }
2146}
2147
2148impl FromStr for Candidate {
2149 type Err = AttributeError;
2150
2151 fn from_str(s: &str) -> Result<Self, Self::Err> {
2152 let mut i = s.split(' ');
2153
2154 let Some(foundation) = i.next() else {
2155 return Err(AttributeError::ParamNotFound {
2156 param: "Foundation".to_string(),
2157 attr: <Self as TypedAttribute>::NAME.to_string(),
2158 });
2159 };
2160
2161 let Some(comp_id) = i.next() else {
2162 return Err(AttributeError::ParamNotFound {
2163 param: "Component id".to_string(),
2164 attr: <Self as TypedAttribute>::NAME.to_string(),
2165 });
2166 };
2167
2168 let Ok(comp_id) = comp_id.parse::<u32>() else {
2169 return Err(AttributeError::InvalidParamValue {
2170 param: "Component id".to_string(),
2171 val: comp_id.to_string(),
2172 attr: <Self as TypedAttribute>::NAME.to_string(),
2173 });
2174 };
2175
2176 let Some(transport) = i.next() else {
2177 return Err(AttributeError::ParamNotFound {
2178 param: "Transport".to_string(),
2179 attr: <Self as TypedAttribute>::NAME.to_string(),
2180 });
2181 };
2182
2183 let Some(priority) = i.next() else {
2184 return Err(AttributeError::ParamNotFound {
2185 param: "Priority".to_string(),
2186 attr: <Self as TypedAttribute>::NAME.to_string(),
2187 });
2188 };
2189
2190 let Ok(priority) = priority.parse::<u64>() else {
2191 return Err(AttributeError::InvalidParamValue {
2192 param: "Priority".to_string(),
2193 val: priority.to_string(),
2194 attr: <Self as TypedAttribute>::NAME.to_string(),
2195 });
2196 };
2197
2198 let Some(address) = i.next() else {
2199 return Err(AttributeError::ParamNotFound {
2200 param: "Address".to_string(),
2201 attr: <Self as TypedAttribute>::NAME.to_string(),
2202 });
2203 };
2204
2205 let address = match address.parse::<IpAddr>() {
2206 Ok(a) => CandidateAddress::IpAddr(a),
2207 Err(_) => CandidateAddress::FQDN(address.to_string()),
2208 };
2209
2210 let Some(port) = i.next() else {
2211 return Err(AttributeError::ParamNotFound {
2212 param: "Port".to_string(),
2213 attr: <Self as TypedAttribute>::NAME.to_string(),
2214 });
2215 };
2216
2217 let Ok(port) = port.parse::<u16>() else {
2218 return Err(AttributeError::InvalidParamValue {
2219 param: "Port".to_string(),
2220 val: port.to_string(),
2221 attr: <Self as TypedAttribute>::NAME.to_string(),
2222 });
2223 };
2224
2225 let Some(typ_str) = i.next() else {
2226 return Err(AttributeError::ParamNotFound {
2227 param: "'typ' string".to_string(),
2228 attr: <Self as TypedAttribute>::NAME.to_string(),
2229 });
2230 };
2231
2232 if !typ_str.eq_ignore_ascii_case("typ") {
2233 return Err(AttributeError::ParamNotFound {
2234 param: "'typ' string".to_string(),
2235 attr: <Self as TypedAttribute>::NAME.to_string(),
2236 });
2237 }
2238
2239 let Some(cand_type) = i.next() else {
2240 return Err(AttributeError::ParamNotFound {
2241 param: "Candidate type".to_string(),
2242 attr: <Self as TypedAttribute>::NAME.to_string(),
2243 });
2244 };
2245
2246 let cand_type = CandidateType::new(cand_type);
2247
2248 let mut rel_addr: Option<IpAddr> = None;
2249 let mut rel_port: Option<u16> = None;
2250 let mut exts: Vec<(String, String)> = Vec::new();
2251
2252 while let Some(key) = i.next() {
2253 if key.eq_ignore_ascii_case("raddr") {
2254 let Some(raddr) = i.next() else {
2255 return Err(AttributeError::ParamNotFound {
2256 param: "Relative address".to_string(),
2257 attr: <Self as TypedAttribute>::NAME.to_string(),
2258 });
2259 };
2260
2261 if let Ok(raddr) = raddr.parse::<IpAddr>() {
2262 rel_addr = Some(raddr);
2263 } else {
2264 return Err(AttributeError::InvalidParamValue {
2265 param: "Relative address".to_string(),
2266 val: raddr.to_string(),
2267 attr: <Self as TypedAttribute>::NAME.to_string(),
2268 });
2269 };
2270 } else if key.eq_ignore_ascii_case("rport") {
2271 let Some(rport) = i.next() else {
2272 return Err(AttributeError::ParamNotFound {
2273 param: "Relative port".to_string(),
2274 attr: <Self as TypedAttribute>::NAME.to_string(),
2275 });
2276 };
2277
2278 if let Ok(rport) = rport.parse::<u16>() {
2279 rel_port = Some(rport);
2280 } else {
2281 return Err(AttributeError::InvalidParamValue {
2282 param: "Relative port".to_string(),
2283 val: rport.to_string(),
2284 attr: <Self as TypedAttribute>::NAME.to_string(),
2285 });
2286 }
2287 } else {
2288 let Some(val) = i.next() else {
2289 return Err(AttributeError::Other {
2290 error: format!("No val for the extension {key}"),
2291 attr: <Self as TypedAttribute>::NAME.to_string(),
2292 });
2293 };
2294
2295 exts.push((key.to_string(), val.to_string()));
2296 }
2297 }
2298
2299 Ok(Self {
2300 foundation: foundation.to_string(),
2301 component_id: comp_id,
2302 transport: transport.to_string(),
2303 priority,
2304 address,
2305 port,
2306 typ: cand_type,
2307 rel_addr,
2308 rel_port,
2309 extensions: exts,
2310 })
2311 }
2312}
2313
2314impl Display for Candidate {
2315 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2316 let candidate_addr = match &self.address {
2317 CandidateAddress::IpAddr(a) => a.to_string(),
2318 CandidateAddress::FQDN(d) => d.clone(),
2319 };
2320 write!(
2321 f,
2322 "{} {} {} {} {} {} typ {}",
2323 self.foundation,
2324 self.component_id,
2325 self.transport,
2326 self.priority,
2327 candidate_addr,
2328 self.port,
2329 self.typ.as_str(),
2330 )?;
2331 if let Some(rel_addr) = self.rel_addr {
2332 write!(f, " raddr {rel_addr}")?;
2333 }
2334 if let Some(rel_port) = self.rel_port {
2335 write!(f, " rport {rel_port}")?;
2336 }
2337 for (key, val) in &self.extensions {
2338 write!(f, " {key} {val}")?;
2339 }
2340 Ok(())
2341 }
2342}
2343
2344impl TypedAttribute for Candidate {
2345 const NAME: &'static str = "candidate";
2346}
2347
2348#[derive(Debug, Clone, PartialEq, Eq)]
2357#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2358pub struct Rid {
2359 pub id: String,
2361 pub direction: RidDirection,
2363 pub params: Vec<String>,
2365}
2366
2367impl Rid {
2368 pub fn new(id: impl ToString, direction: RidDirection) -> Self {
2369 Rid {
2370 id: id.to_string(),
2371 direction,
2372 params: Vec::new(),
2373 }
2374 }
2375
2376 pub fn add_param(&mut self, param: impl ToString) {
2377 self.params.push(param.to_string());
2378 }
2379
2380 pub fn with_params(
2381 id: impl ToString,
2382 direction: RidDirection,
2383 params: impl IntoIterator<Item = impl ToString>,
2384 ) -> Self {
2385 Rid {
2386 id: id.to_string(),
2387 direction,
2388 params: params.into_iter().map(|p| p.to_string()).collect(),
2389 }
2390 }
2391}
2392
2393impl FromStr for Rid {
2394 type Err = AttributeError;
2395
2396 fn from_str(s: &str) -> Result<Self, Self::Err> {
2397 let attr = <Self as TypedAttribute>::NAME.to_string();
2398 let (id, rest) = s
2399 .split_once(' ')
2400 .ok_or_else(|| AttributeError::ParamNotFound {
2401 param: "Direction".to_string(),
2402 attr: attr.clone(),
2403 })?;
2404
2405 let id = id.trim();
2406 if id.is_empty() {
2407 return Err(AttributeError::ParamNotFound {
2408 param: "ID".to_string(),
2409 attr: attr.clone(),
2410 });
2411 }
2412
2413 if !id
2416 .bytes()
2417 .all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
2418 {
2419 return Err(AttributeError::InvalidParamValue {
2420 param: "ID".to_string(),
2421 val: id.to_string(),
2422 attr,
2423 });
2424 }
2425
2426 let (direction_str, params_str) = match rest.trim().split_once(' ') {
2429 Some((d, p)) => (d, p),
2430 None => (rest.trim(), ""),
2431 };
2432
2433 let direction = direction_str
2434 .parse()
2435 .map_err(|_| AttributeError::InvalidParamValue {
2436 param: "Direction".to_string(),
2437 val: direction_str.to_string(),
2438 attr: attr.clone(),
2439 })?;
2440
2441 let params = if params_str.trim().is_empty() {
2442 Vec::new()
2443 } else {
2444 params_str
2445 .trim()
2446 .split(';')
2447 .map(|p| p.trim())
2448 .filter(|p| !p.is_empty())
2449 .map(|p| {
2450 if p.contains(char::is_whitespace) {
2451 Err(AttributeError::InvalidParamValue {
2452 param: "Parameter".to_string(),
2453 val: p.to_string(),
2454 attr: attr.clone(),
2455 })
2456 } else {
2457 Ok(p.to_string())
2458 }
2459 })
2460 .collect::<Result<Vec<_>, _>>()?
2461 };
2462
2463 Ok(Rid {
2464 id: id.to_string(),
2465 direction,
2466 params,
2467 })
2468 }
2469}
2470
2471impl Display for Rid {
2472 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2473 write!(f, "{} {}", self.id, self.direction)?;
2474 if !self.params.is_empty() {
2475 write!(f, " {}", self.params.join(";"))?;
2476 }
2477 Ok(())
2478 }
2479}
2480
2481impl TypedAttribute for Rid {
2482 const NAME: &'static str = "rid";
2483}
2484
2485#[derive(Debug, Clone, PartialEq, Eq)]
2492#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2493pub enum KeyMgmtMethod {
2494 Mikey,
2496 Other(String),
2498}
2499
2500impl From<&str> for KeyMgmtMethod {
2501 fn from(s: &str) -> Self {
2502 match s {
2503 "mikey" => KeyMgmtMethod::Mikey,
2504 other => KeyMgmtMethod::Other(other.to_string()),
2505 }
2506 }
2507}
2508
2509impl std::str::FromStr for KeyMgmtMethod {
2510 type Err = ();
2512
2513 fn from_str(s: &str) -> Result<Self, Self::Err> {
2514 Ok(s.into())
2515 }
2516}
2517
2518impl Display for KeyMgmtMethod {
2519 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2520 match self {
2521 KeyMgmtMethod::Mikey => f.write_str("mikey"),
2522 KeyMgmtMethod::Other(other) => f.write_str(other),
2523 }
2524 }
2525}
2526
2527#[derive(Debug, Clone, PartialEq, Eq)]
2536#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2537pub struct KeyMgmt {
2538 pub method: KeyMgmtMethod,
2540 pub data: Option<String>,
2542}
2543
2544impl KeyMgmt {
2545 pub fn new(method: KeyMgmtMethod) -> Self {
2546 KeyMgmt { method, data: None }
2547 }
2548
2549 pub fn with_data(method: KeyMgmtMethod, data: impl ToString) -> Self {
2550 KeyMgmt {
2551 method,
2552 data: Some(data.to_string()),
2553 }
2554 }
2555}
2556
2557impl FromStr for KeyMgmt {
2558 type Err = AttributeError;
2559
2560 fn from_str(s: &str) -> Result<Self, Self::Err> {
2561 let s = s.trim();
2562 if s.is_empty() {
2563 return Err(AttributeError::ParamNotFound {
2564 param: "Method".to_string(),
2565 attr: <Self as TypedAttribute>::NAME.to_string(),
2566 });
2567 }
2568
2569 let (method, data) = match s.split_once(char::is_whitespace) {
2570 Some((method, data)) => (method, Some(data.trim().to_string())),
2571 None => (s, None),
2572 };
2573
2574 if !method.bytes().all(|b| b.is_ascii_alphanumeric()) {
2576 return Err(AttributeError::InvalidParamValue {
2577 param: "Method".to_string(),
2578 val: method.to_string(),
2579 attr: <Self as TypedAttribute>::NAME.to_string(),
2580 });
2581 }
2582
2583 Ok(KeyMgmt {
2584 method: method.into(),
2585 data,
2586 })
2587 }
2588}
2589
2590impl Display for KeyMgmt {
2591 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2592 match &self.data {
2593 Some(data) => write!(f, "{} {}", self.method, data),
2594 None => write!(f, "{}", self.method),
2595 }
2596 }
2597}
2598
2599impl TypedAttribute for KeyMgmt {
2600 const NAME: &'static str = "key-mgmt";
2601}
2602
2603#[derive(Debug, Clone, PartialEq, Eq)]
2613#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2614pub struct RemoteCandidate {
2615 pub component_id: u32,
2617 pub address: IpAddr,
2619 pub port: u16,
2621}
2622
2623impl RemoteCandidate {
2624 pub fn new(component_id: u32, address: impl Into<IpAddr>, port: u16) -> Self {
2625 RemoteCandidate {
2626 component_id,
2627 address: address.into(),
2628 port,
2629 }
2630 }
2631}
2632
2633impl FromStr for RemoteCandidate {
2634 type Err = AttributeError;
2635
2636 fn from_str(s: &str) -> Result<Self, Self::Err> {
2637 let attr = <RemoteCandidates as TypedAttribute>::NAME.to_string();
2638 let mut parts = s.split_whitespace();
2639
2640 let component_id_token = parts.next().ok_or_else(|| AttributeError::ParamNotFound {
2641 param: "Component ID".to_string(),
2642 attr: attr.clone(),
2643 })?;
2644 let component_id =
2645 component_id_token
2646 .parse()
2647 .map_err(|_| AttributeError::InvalidParamValue {
2648 param: "Component ID".to_string(),
2649 val: component_id_token.to_string(),
2650 attr: attr.clone(),
2651 })?;
2652
2653 let address_token = parts.next().ok_or_else(|| AttributeError::ParamNotFound {
2654 param: "Address".to_string(),
2655 attr: attr.clone(),
2656 })?;
2657 let address = address_token
2658 .parse()
2659 .map_err(|_| AttributeError::InvalidParamValue {
2660 param: "Address".to_string(),
2661 val: address_token.to_string(),
2662 attr: attr.clone(),
2663 })?;
2664
2665 let port_token = parts.next().ok_or_else(|| AttributeError::ParamNotFound {
2666 param: "Port".to_string(),
2667 attr: attr.clone(),
2668 })?;
2669 let port = port_token
2670 .parse()
2671 .map_err(|_| AttributeError::InvalidParamValue {
2672 param: "Port".to_string(),
2673 val: port_token.to_string(),
2674 attr: attr.clone(),
2675 })?;
2676
2677 if let Some(val) = parts.next() {
2678 return Err(AttributeError::UnexpectedTrailingItem {
2679 val: val.to_string(),
2680 attr,
2681 });
2682 }
2683
2684 Ok(RemoteCandidate {
2685 component_id,
2686 address,
2687 port,
2688 })
2689 }
2690}
2691
2692impl Display for RemoteCandidate {
2693 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2694 write!(f, "{} {} {}", self.component_id, self.address, self.port)
2695 }
2696}
2697
2698#[derive(Debug, Clone, PartialEq, Eq)]
2709#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2710pub struct RemoteCandidates(pub Vec<RemoteCandidate>);
2711
2712impl RemoteCandidates {
2713 pub fn new(candidates: impl IntoIterator<Item = RemoteCandidate>) -> Self {
2714 RemoteCandidates(candidates.into_iter().collect())
2715 }
2716}
2717
2718impl FromStr for RemoteCandidates {
2719 type Err = AttributeError;
2720
2721 fn from_str(s: &str) -> Result<Self, Self::Err> {
2722 let attr = <Self as TypedAttribute>::NAME.to_string();
2723 let mut tokens = s.split_whitespace();
2724 let mut candidates = Vec::new();
2725
2726 loop {
2728 let triple: Vec<&str> = tokens.by_ref().take(3).collect();
2729 match triple.len() {
2730 0 => break,
2731 1..=2 => {
2732 return Err(AttributeError::UnexpectedTrailingItem {
2733 val: triple.join(" "),
2734 attr,
2735 })
2736 }
2737 _ => candidates.push(triple.join(" ").parse()?),
2738 }
2739 }
2740
2741 if candidates.is_empty() {
2742 return Err(AttributeError::ParamNotFound {
2743 param: "Candidate".to_string(),
2744 attr,
2745 });
2746 }
2747
2748 Ok(RemoteCandidates(candidates))
2749 }
2750}
2751
2752impl Display for RemoteCandidates {
2753 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2754 for (i, candidate) in self.0.iter().enumerate() {
2755 if i > 0 {
2756 f.write_char(' ')?;
2757 }
2758 write!(f, "{}", candidate)?;
2759 }
2760 Ok(())
2761 }
2762}
2763
2764impl TypedAttribute for RemoteCandidates {
2765 const NAME: &'static str = "remote-candidates";
2766}
2767
2768#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2772#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2773pub enum SourceFilterMode {
2774 Incl,
2776 Excl,
2778}
2779
2780impl SourceFilterMode {
2781 pub fn as_str(&self) -> &'static str {
2782 match self {
2783 SourceFilterMode::Incl => "incl",
2784 SourceFilterMode::Excl => "excl",
2785 }
2786 }
2787}
2788
2789impl FromStr for SourceFilterMode {
2790 type Err = ParseEnumError;
2791
2792 fn from_str(s: &str) -> Result<Self, Self::Err> {
2793 if "incl".eq_ignore_ascii_case(s) {
2794 Ok(SourceFilterMode::Incl)
2795 } else if "excl".eq_ignore_ascii_case(s) {
2796 Ok(SourceFilterMode::Excl)
2797 } else {
2798 Err(ParseEnumError::Invalid(s.to_string()))
2799 }
2800 }
2801}
2802
2803impl Display for SourceFilterMode {
2804 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2805 f.write_str(self.as_str())
2806 }
2807}
2808
2809#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2816#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2817pub enum SourceFilterAddrType {
2818 Any,
2820 Ip4,
2822 Ip6,
2824}
2825
2826impl SourceFilterAddrType {
2827 pub fn as_str(&self) -> &'static str {
2828 match self {
2829 SourceFilterAddrType::Any => "*",
2830 SourceFilterAddrType::Ip4 => "IP4",
2831 SourceFilterAddrType::Ip6 => "IP6",
2832 }
2833 }
2834}
2835
2836impl FromStr for SourceFilterAddrType {
2837 type Err = ParseEnumError;
2838
2839 fn from_str(s: &str) -> Result<Self, Self::Err> {
2840 if s == "*" {
2841 Ok(SourceFilterAddrType::Any)
2842 } else if "IP4".eq_ignore_ascii_case(s) {
2843 Ok(SourceFilterAddrType::Ip4)
2844 } else if "IP6".eq_ignore_ascii_case(s) {
2845 Ok(SourceFilterAddrType::Ip6)
2846 } else {
2847 Err(ParseEnumError::Invalid(s.to_string()))
2848 }
2849 }
2850}
2851
2852impl Display for SourceFilterAddrType {
2853 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2854 f.write_str(self.as_str())
2855 }
2856}
2857
2858#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2865#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2866pub enum SourceFilterAddress {
2867 IpAddr(IpAddr),
2869 FQDN(String),
2871}
2872
2873impl From<&str> for SourceFilterAddress {
2874 fn from(s: &str) -> Self {
2875 match s.parse::<IpAddr>() {
2876 Ok(address) => SourceFilterAddress::IpAddr(address),
2877 Err(_) => SourceFilterAddress::FQDN(s.to_string()),
2878 }
2879 }
2880}
2881
2882impl FromStr for SourceFilterAddress {
2883 type Err = ();
2885
2886 fn from_str(s: &str) -> Result<Self, Self::Err> {
2887 Ok(s.into())
2888 }
2889}
2890
2891impl Display for SourceFilterAddress {
2892 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2893 match self {
2894 SourceFilterAddress::IpAddr(address) => write!(f, "{}", address),
2895 SourceFilterAddress::FQDN(domain) => f.write_str(domain),
2896 }
2897 }
2898}
2899
2900#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2905#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2906pub enum SourceFilterDestination {
2907 Wildcard,
2909 Address(SourceFilterAddress),
2911}
2912
2913impl FromStr for SourceFilterDestination {
2914 type Err = ();
2916
2917 fn from_str(s: &str) -> Result<Self, Self::Err> {
2918 if s == "*" {
2919 Ok(SourceFilterDestination::Wildcard)
2920 } else {
2921 Ok(SourceFilterDestination::Address(s.into()))
2922 }
2923 }
2924}
2925
2926impl Display for SourceFilterDestination {
2927 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2928 match self {
2929 SourceFilterDestination::Wildcard => f.write_str("*"),
2930 SourceFilterDestination::Address(address) => write!(f, "{}", address),
2931 }
2932 }
2933}
2934
2935#[derive(Debug, Clone, PartialEq, Eq)]
2948#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2949pub struct SourceFilter {
2950 pub mode: SourceFilterMode,
2952 pub nettype: NetType,
2954 pub addrtype: SourceFilterAddrType,
2956 pub destination: SourceFilterDestination,
2958 pub sources: Vec<SourceFilterAddress>,
2960}
2961
2962impl FromStr for SourceFilter {
2963 type Err = AttributeError;
2964
2965 fn from_str(s: &str) -> Result<Self, Self::Err> {
2966 let attr = <Self as TypedAttribute>::NAME.to_string();
2967
2968 let mut i = s.split(' ').filter(|part| !part.is_empty());
2971
2972 let Some(mode) = i.next() else {
2973 return Err(AttributeError::ParamNotFound {
2974 param: "Filter mode".to_string(),
2975 attr,
2976 });
2977 };
2978 let mode = mode
2979 .parse()
2980 .map_err(|_| AttributeError::InvalidParamValue {
2981 param: "Filter mode".to_string(),
2982 val: mode.to_string(),
2983 attr: attr.clone(),
2984 })?;
2985
2986 let Some(nettype) = i.next() else {
2987 return Err(AttributeError::ParamNotFound {
2988 param: "Network type".to_string(),
2989 attr,
2990 });
2991 };
2992 if !is_token(nettype.as_bytes()) {
2994 return Err(AttributeError::InvalidParamValue {
2995 param: "Network type".to_string(),
2996 val: nettype.to_string(),
2997 attr: attr.clone(),
2998 });
2999 }
3000 let nettype = NetType::from_str(nettype).expect("infallible");
3001
3002 let Some(addrtype) = i.next() else {
3003 return Err(AttributeError::ParamNotFound {
3004 param: "Address types".to_string(),
3005 attr,
3006 });
3007 };
3008 let addrtype = addrtype
3009 .parse()
3010 .map_err(|_| AttributeError::InvalidParamValue {
3011 param: "Address types".to_string(),
3012 val: addrtype.to_string(),
3013 attr: attr.clone(),
3014 })?;
3015
3016 let Some(destination) = i.next() else {
3017 return Err(AttributeError::ParamNotFound {
3018 param: "Destination address".to_string(),
3019 attr,
3020 });
3021 };
3022
3023 if destination.contains('/') {
3026 return Err(AttributeError::InvalidParamValue {
3027 param: "Destination address".to_string(),
3028 val: destination.to_string(),
3029 attr,
3030 });
3031 }
3032 let destination = SourceFilterDestination::from_str(destination).expect("infallible");
3033 if addrtype == SourceFilterAddrType::Any
3036 && matches!(
3037 destination,
3038 SourceFilterDestination::Address(SourceFilterAddress::IpAddr(_))
3039 )
3040 {
3041 return Err(AttributeError::InvalidParamValue {
3042 param: "Destination address".to_string(),
3043 val: destination.to_string(),
3044 attr,
3045 });
3046 }
3047
3048 let sources: Vec<&str> = i.collect();
3049 if sources.is_empty() {
3050 return Err(AttributeError::ParamNotFound {
3051 param: "Source list".to_string(),
3052 attr,
3053 });
3054 }
3055 if let Some(source) = sources.iter().find(|source| source == &&"*") {
3057 return Err(AttributeError::InvalidParamValue {
3058 param: "Source address".to_string(),
3059 val: source.to_string(),
3060 attr,
3061 });
3062 }
3063 let sources = sources.into_iter().map(SourceFilterAddress::from).collect();
3064
3065 Ok(SourceFilter {
3066 mode,
3067 nettype,
3068 addrtype,
3069 destination,
3070 sources,
3071 })
3072 }
3073}
3074
3075impl Display for SourceFilter {
3076 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3077 write!(
3078 f,
3079 "{} {} {} {}",
3080 self.mode, self.nettype, self.addrtype, self.destination
3081 )?;
3082 for source in &self.sources {
3083 write!(f, " {}", source)?;
3084 }
3085 Ok(())
3086 }
3087}
3088
3089impl TypedAttribute for SourceFilter {
3090 const NAME: &'static str = "source-filter";
3091}
3092
3093#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3101#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3102pub struct Mid(pub String);
3103
3104impl FromStr for Mid {
3105 type Err = AttributeError;
3106
3107 fn from_str(s: &str) -> Result<Self, Self::Err> {
3108 if !is_token(s.as_bytes()) {
3109 return Err(AttributeError::InvalidParamValue {
3110 param: "Identification tag".to_string(),
3111 val: s.to_string(),
3112 attr: <Self as TypedAttribute>::NAME.to_string(),
3113 });
3114 }
3115
3116 Ok(Mid(s.to_string()))
3117 }
3118}
3119
3120impl Display for Mid {
3121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3122 f.write_str(&self.0)
3123 }
3124}
3125
3126impl TypedAttribute for Mid {
3127 const NAME: &'static str = "mid";
3128}
3129
3130#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3138#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3139pub struct RtcpMux;
3140
3141impl FromStr for RtcpMux {
3142 type Err = AttributeError;
3143
3144 fn from_str(s: &str) -> Result<Self, Self::Err> {
3145 if !s.is_empty() {
3146 return Err(AttributeError::UnsupportedFormat {
3147 val: s.to_string(),
3148 attr: <Self as TypedAttribute>::NAME.to_string(),
3149 });
3150 }
3151
3152 Ok(RtcpMux)
3153 }
3154}
3155
3156impl Display for RtcpMux {
3157 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3158 Ok(())
3159 }
3160}
3161
3162impl TypedAttribute for RtcpMux {
3163 const NAME: &'static str = "rtcp-mux";
3164}
3165
3166#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3176pub struct RtcpMuxOnly;
3177
3178impl FromStr for RtcpMuxOnly {
3179 type Err = AttributeError;
3180
3181 fn from_str(s: &str) -> Result<Self, Self::Err> {
3182 if !s.is_empty() {
3183 return Err(AttributeError::UnsupportedFormat {
3184 val: s.to_string(),
3185 attr: <Self as TypedAttribute>::NAME.to_string(),
3186 });
3187 }
3188
3189 Ok(RtcpMuxOnly)
3190 }
3191}
3192
3193impl Display for RtcpMuxOnly {
3194 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3195 Ok(())
3196 }
3197}
3198
3199impl TypedAttribute for RtcpMuxOnly {
3200 const NAME: &'static str = "rtcp-mux-only";
3201}
3202
3203#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3211#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3212pub struct RtcpRsize;
3213
3214impl FromStr for RtcpRsize {
3215 type Err = AttributeError;
3216
3217 fn from_str(s: &str) -> Result<Self, Self::Err> {
3218 if !s.is_empty() {
3219 return Err(AttributeError::UnsupportedFormat {
3220 val: s.to_string(),
3221 attr: <Self as TypedAttribute>::NAME.to_string(),
3222 });
3223 }
3224
3225 Ok(RtcpRsize)
3226 }
3227}
3228
3229impl Display for RtcpRsize {
3230 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3231 Ok(())
3232 }
3233}
3234
3235impl TypedAttribute for RtcpRsize {
3236 const NAME: &'static str = "rtcp-rsize";
3237}
3238
3239#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3247#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3248pub struct IceLite;
3249
3250impl FromStr for IceLite {
3251 type Err = AttributeError;
3252
3253 fn from_str(s: &str) -> Result<Self, Self::Err> {
3254 if !s.is_empty() {
3255 return Err(AttributeError::UnsupportedFormat {
3256 val: s.to_string(),
3257 attr: <Self as TypedAttribute>::NAME.to_string(),
3258 });
3259 }
3260
3261 Ok(IceLite)
3262 }
3263}
3264
3265impl Display for IceLite {
3266 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3267 Ok(())
3268 }
3269}
3270
3271impl TypedAttribute for IceLite {
3272 const NAME: &'static str = "ice-lite";
3273}
3274
3275fn is_ice_char(c: char) -> bool {
3277 matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '/')
3278}
3279
3280#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3288#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3289pub struct IceUfrag(pub String);
3290
3291impl FromStr for IceUfrag {
3292 type Err = AttributeError;
3293
3294 fn from_str(s: &str) -> Result<Self, Self::Err> {
3295 if !(4..=256).contains(&s.len()) || !s.chars().all(is_ice_char) {
3297 return Err(AttributeError::InvalidParamValue {
3298 param: "ICE username fragment".to_string(),
3299 val: s.to_string(),
3300 attr: <Self as TypedAttribute>::NAME.to_string(),
3301 });
3302 }
3303
3304 Ok(IceUfrag(s.to_string()))
3305 }
3306}
3307
3308impl Display for IceUfrag {
3309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3310 f.write_str(&self.0)
3311 }
3312}
3313
3314impl TypedAttribute for IceUfrag {
3315 const NAME: &'static str = "ice-ufrag";
3316}
3317
3318#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3326#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3327pub struct IcePwd(pub String);
3328
3329impl FromStr for IcePwd {
3330 type Err = AttributeError;
3331
3332 fn from_str(s: &str) -> Result<Self, Self::Err> {
3333 if !(22..=256).contains(&s.len()) || !s.chars().all(is_ice_char) {
3335 return Err(AttributeError::InvalidParamValue {
3336 param: "ICE password".to_string(),
3337 val: s.to_string(),
3338 attr: <Self as TypedAttribute>::NAME.to_string(),
3339 });
3340 }
3341
3342 Ok(IcePwd(s.to_string()))
3343 }
3344}
3345
3346impl Display for IcePwd {
3347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3348 f.write_str(&self.0)
3349 }
3350}
3351
3352impl TypedAttribute for IcePwd {
3353 const NAME: &'static str = "ice-pwd";
3354}
3355
3356#[derive(Debug, Clone, PartialEq, Eq)]
3363#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3364pub struct IceOptions(pub Vec<String>);
3365
3366impl IceOptions {
3367 pub fn new(tags: impl IntoIterator<Item = impl ToString>) -> Self {
3368 IceOptions(tags.into_iter().map(|tag| tag.to_string()).collect())
3369 }
3370}
3371
3372impl FromStr for IceOptions {
3373 type Err = AttributeError;
3374
3375 fn from_str(s: &str) -> Result<Self, Self::Err> {
3376 let attr = <Self as TypedAttribute>::NAME.to_string();
3377 let mut tags = Vec::new();
3378
3379 for tag in s.split_whitespace() {
3380 if !tag.chars().all(is_ice_char) {
3381 return Err(AttributeError::InvalidParamValue {
3382 param: "ICE option tag".to_string(),
3383 val: tag.to_string(),
3384 attr,
3385 });
3386 }
3387
3388 tags.push(tag.to_string());
3389 }
3390
3391 if tags.is_empty() {
3392 return Err(AttributeError::ParamNotFound {
3393 param: "ICE option tags".to_string(),
3394 attr,
3395 });
3396 }
3397
3398 Ok(IceOptions(tags))
3399 }
3400}
3401
3402impl Display for IceOptions {
3403 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3404 for (i, tag) in self.0.iter().enumerate() {
3405 if i > 0 {
3406 f.write_char(' ')?;
3407 }
3408 f.write_str(tag)?;
3409 }
3410 Ok(())
3411 }
3412}
3413
3414impl TypedAttribute for IceOptions {
3415 const NAME: &'static str = "ice-options";
3416}
3417
3418#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3426#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3427pub struct EndOfCandidates;
3428
3429impl FromStr for EndOfCandidates {
3430 type Err = AttributeError;
3431
3432 fn from_str(s: &str) -> Result<Self, Self::Err> {
3433 if !s.is_empty() {
3434 return Err(AttributeError::UnsupportedFormat {
3435 val: s.to_string(),
3436 attr: <Self as TypedAttribute>::NAME.to_string(),
3437 });
3438 }
3439
3440 Ok(EndOfCandidates)
3441 }
3442}
3443
3444impl Display for EndOfCandidates {
3445 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3446 Ok(())
3447 }
3448}
3449
3450impl TypedAttribute for EndOfCandidates {
3451 const NAME: &'static str = "end-of-candidates";
3452}
3453
3454#[derive(Debug, Clone, PartialEq, Eq)]
3462#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3463pub struct Msid {
3464 pub id: String,
3466 pub appdata: Option<String>,
3468}
3469
3470impl Msid {
3471 pub fn new(id: impl ToString, appdata: Option<impl ToString>) -> Self {
3472 Msid {
3473 id: id.to_string(),
3474 appdata: appdata.map(|appdata| appdata.to_string()),
3475 }
3476 }
3477}
3478
3479impl FromStr for Msid {
3480 type Err = AttributeError;
3481
3482 fn from_str(s: &str) -> Result<Self, Self::Err> {
3483 let attr = <Self as TypedAttribute>::NAME.to_string();
3484 let mut tokens = s.split_whitespace();
3485
3486 let id = tokens.next().ok_or_else(|| AttributeError::ParamNotFound {
3487 param: "MSID id".to_string(),
3488 attr: attr.clone(),
3489 })?;
3490 let appdata = tokens.next();
3491
3492 let trailing: Vec<_> = tokens.collect();
3493 if !trailing.is_empty() {
3494 return Err(AttributeError::UnexpectedTrailingItem {
3495 val: trailing.join(" "),
3496 attr,
3497 });
3498 }
3499
3500 if id.len() > 64 || !is_token(id.as_bytes()) {
3502 return Err(AttributeError::InvalidParamValue {
3503 param: "MSID id".to_string(),
3504 val: id.to_string(),
3505 attr,
3506 });
3507 }
3508
3509 if let Some(appdata) = appdata {
3510 if appdata.len() > 64 || !is_token(appdata.as_bytes()) {
3511 return Err(AttributeError::InvalidParamValue {
3512 param: "MSID appdata".to_string(),
3513 val: appdata.to_string(),
3514 attr,
3515 });
3516 }
3517 }
3518
3519 Ok(Msid {
3520 id: id.to_string(),
3521 appdata: appdata.map(ToString::to_string),
3522 })
3523 }
3524}
3525
3526impl Display for Msid {
3527 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3528 f.write_str(&self.id)?;
3529 if let Some(appdata) = &self.appdata {
3530 f.write_char(' ')?;
3531 f.write_str(appdata)?;
3532 }
3533 Ok(())
3534 }
3535}
3536
3537impl TypedAttribute for Msid {
3538 const NAME: &'static str = "msid";
3539}
3540
3541#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3550#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3551pub struct SctpPort(pub u16);
3552
3553fn parse_1_5_digits(s: &str) -> Option<u16> {
3555 if s.is_empty()
3556 || s.len() > 5
3557 || !s.chars().all(|c| c.is_ascii_digit())
3558 || (s.len() > 1 && s.starts_with('0'))
3559 {
3560 return None;
3561 }
3562
3563 s.parse().ok()
3564}
3565
3566impl FromStr for SctpPort {
3567 type Err = AttributeError;
3568
3569 fn from_str(s: &str) -> Result<Self, Self::Err> {
3570 let invalid = || AttributeError::InvalidParamValue {
3571 param: "SCTP port".to_string(),
3572 val: s.to_string(),
3573 attr: <Self as TypedAttribute>::NAME.to_string(),
3574 };
3575
3576 let port = parse_1_5_digits(s).ok_or_else(invalid)?;
3578 Ok(SctpPort(port))
3579 }
3580}
3581
3582impl Display for SctpPort {
3583 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3584 write!(f, "{}", self.0)
3585 }
3586}
3587
3588impl TypedAttribute for SctpPort {
3589 const NAME: &'static str = "sctp-port";
3590}
3591
3592#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3601#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3602pub struct MaxMessageSize(pub u64);
3603
3604impl FromStr for MaxMessageSize {
3605 type Err = AttributeError;
3606
3607 fn from_str(s: &str) -> Result<Self, Self::Err> {
3608 let invalid = || AttributeError::InvalidParamValue {
3609 param: "Max message size".to_string(),
3610 val: s.to_string(),
3611 attr: <Self as TypedAttribute>::NAME.to_string(),
3612 };
3613
3614 let size = parse_decimal_no_leading_zeroes(s).ok_or_else(invalid)?;
3616 Ok(MaxMessageSize(size))
3617 }
3618}
3619
3620impl Display for MaxMessageSize {
3621 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3622 write!(f, "{}", self.0)
3623 }
3624}
3625
3626impl TypedAttribute for MaxMessageSize {
3627 const NAME: &'static str = "max-message-size";
3628}
3629
3630#[derive(Debug, Clone, PartialEq, Eq)]
3640#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3641pub struct SctpMap {
3642 pub number: u16,
3644 pub app: String,
3647 pub streams: Option<u16>,
3649}
3650
3651impl SctpMap {
3652 pub fn new(number: u16, app: impl ToString, streams: Option<u16>) -> Self {
3653 SctpMap {
3654 number,
3655 app: app.to_string(),
3656 streams,
3657 }
3658 }
3659}
3660
3661impl FromStr for SctpMap {
3662 type Err = AttributeError;
3663
3664 fn from_str(s: &str) -> Result<Self, Self::Err> {
3665 let attr = <Self as TypedAttribute>::NAME.to_string();
3666 let mut tokens = s.split_whitespace();
3667
3668 let number = tokens.next().ok_or_else(|| AttributeError::ParamNotFound {
3669 param: "SCTP port number".to_string(),
3670 attr: attr.clone(),
3671 })?;
3672 let number = parse_1_5_digits(number).ok_or_else(|| AttributeError::InvalidParamValue {
3673 param: "SCTP port number".to_string(),
3674 val: number.to_string(),
3675 attr: attr.clone(),
3676 })?;
3677
3678 let app = tokens.next().ok_or_else(|| AttributeError::ParamNotFound {
3679 param: "Application".to_string(),
3680 attr: attr.clone(),
3681 })?;
3682 if !is_token(app.as_bytes()) {
3683 return Err(AttributeError::InvalidParamValue {
3684 param: "Application".to_string(),
3685 val: app.to_string(),
3686 attr,
3687 });
3688 }
3689
3690 let streams = match tokens.next() {
3691 Some(streams) => Some(parse_1_5_digits(streams).ok_or_else(|| {
3692 AttributeError::InvalidParamValue {
3693 param: "Stream count".to_string(),
3694 val: streams.to_string(),
3695 attr: attr.clone(),
3696 }
3697 })?),
3698 None => None,
3699 };
3700
3701 let trailing: Vec<_> = tokens.collect();
3702 if !trailing.is_empty() {
3703 return Err(AttributeError::UnexpectedTrailingItem {
3704 val: trailing.join(" "),
3705 attr,
3706 });
3707 }
3708
3709 Ok(SctpMap {
3710 number,
3711 app: app.to_string(),
3712 streams,
3713 })
3714 }
3715}
3716
3717impl Display for SctpMap {
3718 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3719 write!(f, "{} {}", self.number, self.app)?;
3720 if let Some(streams) = self.streams {
3721 write!(f, " {}", streams)?;
3722 }
3723 Ok(())
3724 }
3725}
3726
3727impl TypedAttribute for SctpMap {
3728 const NAME: &'static str = "sctpmap";
3729}
3730
3731#[cfg(test)]
3732mod tests {
3733 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3734
3735 use super::*;
3736 use crate::*;
3737
3738 #[test]
3739 fn direction_parse() {
3740 assert_eq!("sendonly".parse::<Direction>(), Ok(Direction::SendOnly));
3741 assert_eq!("recvonly".parse::<Direction>(), Ok(Direction::RecvOnly));
3742 assert_eq!("sendrecv".parse::<Direction>(), Ok(Direction::SendRecv));
3743 assert_eq!("inactive".parse::<Direction>(), Ok(Direction::Inactive));
3744 assert!("invalid".parse::<Direction>().is_err());
3745 }
3746
3747 #[test]
3748 fn direction_display() {
3749 assert_eq!(Direction::SendOnly.to_string(), "sendonly");
3750 assert_eq!(Direction::RecvOnly.to_string(), "recvonly");
3751 assert_eq!(Direction::SendRecv.to_string(), "sendrecv");
3752 assert_eq!(Direction::Inactive.to_string(), "inactive");
3753 }
3754
3755 #[test]
3756 fn parse_rtcp_fb() {
3757 let sdp = "v=0\r
3758o=alice 3203093520 3203093520 IN IP4 host.example.com\r
3759s=Multicast video with feedback\r
3760t=3203130148 3203137348\r
3761m=audio 49170 RTP/AVP 0\r
3762c=IN IP4 224.2.1.183\r
3763a=rtpmap:0 PCMU/8000\r
3764m=video 51372 RTP/AVPF 98 99\r
3765c=IN IP4 224.2.1.184\r
3766a=rtpmap:98 H263-1998/90000\r
3767a=rtpmap:99 H261/90000\r
3768a=rtcp-fb:* nack\r
3769a=rtcp-fb:98 nack rpsi\r
3770a=rtcp-fb:* trr-int 1000\r
3771a=rtcp-fb:98 ccm vbcm 1 2\r
3772a=rtcp-fb:* ccm tmmbr smaxpr=120\r
3773";
3774
3775 let parsed = Session::parse(sdp.as_bytes()).unwrap();
3776 let mut written = vec![];
3777 parsed.write(&mut written).unwrap();
3778
3779 let v = fallible_iterator::convert(parsed.medias[1].attributes_typed::<RtcpFb>())
3780 .collect::<Vec<_>>()
3781 .expect("Valid vector of attributes");
3782 assert_eq!(v[0].pt, RtcpFbPt::Wildcard);
3783 assert_eq!(v[1].val, RtcpFbVal::Nack(Some(RtcpFbNack::Rpsi)));
3784 assert_eq!(v[2].val, RtcpFbVal::TrrInt(1000));
3785 assert_eq!(v[3].val, RtcpFbVal::Ccm(RtcpFbCcm::Vbcm(vec![1, 2])));
3786 assert_eq!(
3787 v[4].val,
3788 RtcpFbVal::Ccm(RtcpFbCcm::Tmmbr(Some("smaxpr=120".to_string())))
3789 );
3790 }
3791
3792 #[test]
3793 fn parse_group_attribute() {
3794 let sdp = "v=0\r
3795o=Laura 289083124 289083124 IN IP4 two.example.com\r
3796c=IN IP4 233.252.0.1/127\r
3797t=0 0\r
3798a=group:LS 1 2\r
3799m=audio 30000 RTP/AVP 0\r
3800a=mid:1\r
3801m=video 30002 RTP/AVP 31\r
3802a=mid:2\r
3803m=audio 30004 RTP/AVP 0\r
3804i=This media stream contains the Spanish translation\r
3805a=mid:3\r
3806";
3807 let parsed = Session::parse(sdp.as_bytes()).unwrap();
3808
3809 let g = parsed.attributes_typed::<Group>().collect::<Vec<_>>();
3810 assert_eq!(g.len(), 1);
3811 assert_eq!(g[0].as_ref().unwrap().semantics, GroupSemantics::LS);
3812 assert_eq!(
3813 g[0].as_ref().unwrap().mid_tags,
3814 vec!["1".to_string(), "2".to_string()]
3815 );
3816 }
3817
3818 #[test]
3819 fn parse_setup_attribute() {
3820 let sdp = "v=0\r
3821m=image 54111 TCP t38\r
3822c=IN IP4 192.0.2.2\r
3823a=setup:actpass\r
3824a=connection:new\r
3825";
3826 let media = Session::parse(sdp.as_bytes()).unwrap().medias;
3827
3828 let s = media[0].attributes_typed::<Setup>().collect::<Vec<_>>();
3829
3830 assert_eq!(s.len(), 1);
3831 assert_eq!(s[0].as_ref().unwrap().to_owned(), Setup::ActPass);
3832 }
3833
3834 #[test]
3835 fn parse_ssrc_attributes() {
3836 let sdp = "v=0\r
3837o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r
3838m=video 49174 RTP/AVPF 96 98\r
3839a=rtpmap:98 rtx/90000\r
3840a=fmtp:98 apt=96;rtx-time=3000\r
3841a=ssrc-group:FID 11111 22222\r
3842a=ssrc:11111 cname:user3@example.com\r
3843a=ssrc:22222 fmtp:0 0-15\r
3844a=ssrc-group:FID 33333 44444\r
3845a=ssrc:33333 cname:user3@example.com\r
3846a=ssrc:44444 cname:user3@example.com\r
3847a=ssrc:1698359993 rtcp:5003 IN IP4 127.0.0.1
3848";
3849
3850 let parsed = Session::parse(sdp.as_bytes()).unwrap();
3851 let m = &parsed.medias[0];
3852
3853 let ssrcs = m
3854 .attributes_typed::<Ssrc>()
3855 .filter(|s| {
3856 let Ok(ssrc) = s else { return false };
3857 ssrc.attribute == SsrcAttribute::Fmtp || ssrc.attribute == SsrcAttribute::Rtcp
3858 })
3859 .collect::<Vec<_>>();
3860
3861 let ssrc_id = ssrcs[0].as_ref().unwrap().ssrc_id;
3862
3863 let ssrc_groups = m
3864 .attributes_typed::<SsrcGroup>()
3865 .filter(|s| {
3866 let Ok(ssrc_group) = s else { return false };
3867
3868 ssrc_group.ssrc_ids[1] == ssrc_id
3869 })
3870 .collect::<Vec<_>>();
3871
3872 assert_eq!(
3873 ssrc_groups[0].as_ref().unwrap().semantics,
3874 GroupSemantics::FID
3875 );
3876
3877 assert_eq!(ssrcs[1].as_ref().unwrap().attribute, SsrcAttribute::Rtcp);
3878 }
3879
3880 #[test]
3881 fn parse_crypto_attributes() {
3882 let sdp = "v=0\r
3883o=sam 2890844526 2890842807 IN IP4 10.47.16.5\r
3884s=SRTP Discussion\r
3885i=A discussion of Secure RTP\r
3886u=http://www.example.com/seminars/srtp.pdf\r
3887e=marge@example.com (Marge Simpson)\r
3888c=IN IP4 168.2.17.12\r
3889t=2873397496 2873404696\r
3890m=audio 49170 RTP/SAVP 0\r
3891a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4 FEC_ORDER=SRTP_FEC\r
3892a=crypto:2 F8_128_HMAC_SHA1_80 inline:MTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5QUJjZGVm|2^20|1:4;inline:QUJjZGVmMTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5|2^20|2:4 FEC_ORDER=FEC_SRTP\r
3893m=video 51372 RTP/SAVP 31\r
3894a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2|1066:4\r
3895";
3896
3897 let parsed = Session::parse(sdp.as_bytes()).unwrap();
3898 let a = &parsed.medias[0];
3899
3900 let audio_cryptos = a.attributes_typed::<Crypto>().collect::<Vec<_>>();
3901
3902 assert_eq!(
3903 audio_cryptos[0].as_ref().unwrap().crypto_suite,
3904 CryptoSuite::AesCm128HmacSha1_80
3905 );
3906 assert_eq!(audio_cryptos[1].as_ref().unwrap().key_params.len(), 2);
3907
3908 assert_eq!(
3909 audio_cryptos[1].as_ref().unwrap().key_params[1].mki_and_length,
3910 Some((2, 4))
3911 );
3912
3913 assert_eq!(
3914 audio_cryptos[1].as_ref().unwrap().session_params[0],
3915 SrtpSessionParam::FecOrder(FecOrder::FecSrtp)
3916 );
3917
3918 let v = &parsed.medias[1];
3919
3920 let video_cryptos = v
3921 .attributes_typed::<Crypto>()
3922 .filter(|c| {
3923 let Ok(crypto) = c else { return false };
3924
3925 crypto.tag == 1
3926 })
3927 .collect::<Vec<_>>();
3928
3929 let test_crypto = Crypto {
3930 tag: 1,
3931 crypto_suite: CryptoSuite::AesCm128HmacSha1_80,
3932 key_params: vec![SrtpKeyParam {
3933 key_and_salt: "YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2".to_string(),
3934 lifetime: None,
3935 mki_and_length: Some((1066, 4)),
3936 }],
3937 session_params: Vec::new(),
3938 };
3939
3940 assert_eq!(&test_crypto, video_cryptos[0].as_ref().unwrap());
3941 }
3942
3943 #[test]
3944 fn write_crypto_attribute() {
3945 let crypto = Crypto {
3946 tag: 1,
3947 crypto_suite: CryptoSuite::AesCm128HmacSha1_80,
3948 key_params: vec![
3949 SrtpKeyParam {
3950 key_and_salt: "WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz".to_string(),
3951 lifetime: Some(1048576),
3952 mki_and_length: Some((1, 4)),
3953 },
3954 SrtpKeyParam {
3955 key_and_salt: "WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz".to_string(),
3956 lifetime: Some(1048576),
3957 mki_and_length: Some((1, 4)),
3958 },
3959 ],
3960 session_params: vec![SrtpSessionParam::FecOrder(FecOrder::SrtpFec)],
3961 };
3962
3963 assert_eq!(
3964 crypto.to_string(),
3965 "1 AES_CM_128_HMAC_SHA1_80 inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4;inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4 FEC_ORDER=SRTP_FEC"
3966 );
3967 }
3968
3969 #[test]
3970 fn parse_srtp_key_param_lifetime() {
3971 let param = SrtpKeyParam::from_str("inline:YWJj|2^48").unwrap();
3973 assert_eq!(param.lifetime, Some(1 << 48));
3974 assert_eq!(param.to_string(), "inline:YWJj|2^48");
3975
3976 let param = SrtpKeyParam::from_str("inline:YWJj|1000000000000").unwrap();
3977 assert_eq!(param.lifetime, Some(1_000_000_000_000));
3978 assert_eq!(param.to_string(), "inline:YWJj|1000000000000");
3979
3980 for invalid in [
3981 "inline:YWJj|2^49",
3983 "inline:YWJj|2^100",
3984 "inline:YWJj|281474976710657",
3985 "inline:YWJj|0",
3987 "inline:YWJj|007",
3989 "inline:YWJj|0123",
3990 "inline:YWJj|2^007",
3991 "inline:YWJj|+1048576",
3992 "inline:YWJj|2^+20",
3993 ] {
3994 assert!(
3995 SrtpKeyParam::from_str(invalid).is_err(),
3996 "{} should be invalid",
3997 invalid
3998 );
3999 }
4000 }
4001
4002 #[test]
4003 fn parse_candidate_attributes() {
4004 use std::net::{Ipv4Addr, Ipv6Addr};
4005
4006 let sdp = "v=0\r
4007o=- 2890844526 2890842807 IN IP4 192.168.1.1\r
4008s=-\r
4009c=IN IP4 192.168.1.1\r
4010t=0 0\r
4011m=audio 49152 RTP/AVP 0\r
4012a=candidate:1 1 UDP 2130706432 192.168.1.1 49152 typ host raddr 10.0.1.1 rport 49153 generation 0\r
4013a=candidate:2 1 UDP 1692467200 10.0.1.1 49152 typ srflx raddr 192.168.1.1 rport 49153\r
4014a=candidate:3 2 UDP 1692467184 192.168.1.1 49153 typ host\r
4015a=candidate:4 1 UDP 100 2001:db8::1 49152 typ host\r
4016a=candidate:5 1 UDP 50 192.168.1.1 49154 typ prflx\r
4017a=candidate:6 1 UDP 25 192.168.1.1 49155 typ relay raddr 10.0.0.1 rport 49156\r
4018a=candidate:7 1 UDP 10 192.168.1.1 49157 typ unknown_type\r
4019";
4020
4021 let session = Session::parse(sdp.as_bytes()).unwrap();
4022 let candidates: Vec<Candidate> =
4023 fallible_iterator::convert(session.medias[0].attributes_typed::<Candidate>())
4024 .collect::<Vec<_>>()
4025 .expect("Valid vector of candidates");
4026
4027 assert_eq!(candidates.len(), 7);
4028
4029 assert_eq!(candidates[0].foundation, "1");
4030 assert_eq!(candidates[0].component_id, 1);
4031 assert_eq!(
4032 candidates[0].address,
4033 CandidateAddress::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)))
4034 );
4035 assert_eq!(candidates[0].port, 49152);
4036 assert_eq!(candidates[0].typ, CandidateType::Host);
4037 assert_eq!(
4038 candidates[0].rel_addr,
4039 Some(IpAddr::V4(Ipv4Addr::new(10, 0, 1, 1)))
4040 );
4041 assert_eq!(candidates[0].rel_port, Some(49153));
4042 assert_eq!(
4043 candidates[0].extensions,
4044 vec![("generation".to_string(), "0".to_string())]
4045 );
4046
4047 assert_eq!(candidates[1].foundation, "2");
4048 assert_eq!(candidates[1].typ, CandidateType::Srflx);
4049 assert_eq!(
4050 candidates[1].rel_addr,
4051 Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)))
4052 );
4053 assert_eq!(candidates[1].rel_port, Some(49153));
4054
4055 assert_eq!(candidates[2].foundation, "3");
4056 assert_eq!(candidates[2].component_id, 2);
4057 assert_eq!(candidates[2].typ, CandidateType::Host);
4058
4059 assert_eq!(candidates[3].foundation, "4");
4060 assert_eq!(
4061 candidates[3].address,
4062 CandidateAddress::IpAddr(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)))
4063 );
4064 assert_eq!(candidates[3].typ, CandidateType::Host);
4065
4066 assert_eq!(candidates[4].foundation, "5");
4067 assert_eq!(candidates[4].typ, CandidateType::Prflx);
4068
4069 assert_eq!(candidates[5].foundation, "6");
4070 assert_eq!(candidates[5].typ, CandidateType::Relay);
4071
4072 assert_eq!(candidates[6].foundation, "7");
4073 assert_eq!(
4074 candidates[6].typ,
4075 CandidateType::Other("unknown_type".to_string())
4076 );
4077 }
4078
4079 #[test]
4080 fn write_candidate() {
4081 use std::net::Ipv4Addr;
4082
4083 let candidate = Candidate {
4084 foundation: "abcd/1234".into(),
4085 component_id: 1,
4086 transport: "UDP".into(),
4087 priority: 2130706432,
4088 address: CandidateAddress::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1))),
4089 port: 49152,
4090 typ: CandidateType::Srflx,
4091 rel_addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
4092 rel_port: Some(49153),
4093 extensions: vec![("tcptype".to_string(), "active".to_string())],
4094 };
4095
4096 assert_eq!(
4097 candidate.to_string(),
4098 "abcd/1234 1 UDP 2130706432 192.168.0.1 49152 typ srflx raddr 10.0.0.1 rport 49153 tcptype active"
4099 );
4100 }
4101
4102 #[test]
4103 fn test_attribute_errors() {
4104 assert_eq!(
4106 "99".parse::<RtpMap>().unwrap_err(),
4107 AttributeError::UnsupportedFormat {
4108 val: "99".to_string(),
4109 attr: "rtpmap".to_string()
4110 }
4111 );
4112 assert_eq!(
4113 "abc 90000".parse::<RtpMap>().unwrap_err(),
4114 AttributeError::InvalidParamValue {
4115 param: "Payload type".to_string(),
4116 val: "abc".to_string(),
4117 attr: "rtpmap".to_string()
4118 }
4119 );
4120 assert_eq!(
4121 "200 enc/90000".parse::<RtpMap>().unwrap_err(),
4122 AttributeError::InvalidParamValue {
4123 param: "Payload type".to_string(),
4124 val: "200(expected 0-127)".to_string(),
4125 attr: "rtpmap".to_string()
4126 }
4127 );
4128 assert_eq!(
4129 "99 ".parse::<RtpMap>().unwrap_err(),
4130 AttributeError::ParamNotFound {
4131 param: "Clock rate".to_string(),
4132 attr: "rtpmap".to_string()
4133 }
4134 );
4135 assert_eq!(
4136 "99 /".parse::<RtpMap>().unwrap_err(),
4137 AttributeError::InvalidParamValue {
4138 param: "Clock rate".to_string(),
4139 val: "".to_string(),
4140 attr: "rtpmap".to_string()
4141 }
4142 );
4143
4144 assert_eq!(
4145 "invalid".parse::<Fmtp>().unwrap_err(),
4146 AttributeError::UnsupportedFormat {
4147 val: "invalid".to_string(),
4148 attr: "fmtp".to_string()
4149 }
4150 );
4151 assert_eq!(
4152 "abc profile=1".parse::<Fmtp>().unwrap_err(),
4153 AttributeError::InvalidParamValue {
4154 param: "fmtp".to_string(),
4155 val: "abc".to_string(),
4156 attr: "fmtp".to_string()
4157 }
4158 );
4159
4160 assert_eq!(
4162 "".parse::<Rtcp>().unwrap_err(),
4163 AttributeError::ParamNotFound {
4164 param: "Port".to_string(),
4165 attr: "rtcp".to_string()
4166 }
4167 );
4168 assert_eq!(
4169 "abc IN IP4 127.0.0.1".parse::<Rtcp>().unwrap_err(),
4170 AttributeError::InvalidParamValue {
4171 param: "Port".to_string(),
4172 val: "abc".to_string(),
4173 attr: "rtcp".to_string()
4174 }
4175 );
4176
4177 assert_eq!(
4179 "".parse::<Fingerprint>().unwrap_err(),
4180 AttributeError::ParamNotFound {
4181 param: "Hash value".to_string(),
4182 attr: "fingerprint".to_string()
4183 }
4184 );
4185 assert_eq!(
4186 "SHA-1".parse::<Fingerprint>().unwrap_err(),
4187 AttributeError::ParamNotFound {
4188 param: "Hash value".to_string(),
4189 attr: "fingerprint".to_string()
4190 }
4191 );
4192
4193 assert_eq!(
4195 "".parse::<Candidate>().unwrap_err(),
4196 AttributeError::ParamNotFound {
4197 param: "Component id".to_string(),
4198 attr: "candidate".to_string()
4199 }
4200 );
4201 assert_eq!(
4202 "1 1 UDP 100".parse::<Candidate>().unwrap_err(),
4203 AttributeError::ParamNotFound {
4204 param: "Address".to_string(),
4205 attr: "candidate".to_string()
4206 }
4207 );
4208
4209 assert_eq!(
4211 "".parse::<ExtMap>().unwrap_err(),
4212 AttributeError::InvalidParamValue {
4213 param: "Id".to_string(),
4214 val: "".to_string(),
4215 attr: "extmap".to_string()
4216 }
4217 );
4218 assert_eq!(
4219 "999999 http://example.com".parse::<ExtMap>().unwrap_err(),
4220 AttributeError::InvalidParamValue {
4221 param: "Id".to_string(),
4222 val: "999999".to_string(),
4223 attr: "extmap".to_string()
4224 }
4225 );
4226
4227 assert_eq!(
4229 "".parse::<Group>().unwrap_err(),
4230 AttributeError::ParamNotFound {
4231 param: "Media identification tags".to_string(),
4232 attr: "group".to_string()
4233 }
4234 );
4235 assert_eq!(
4236 "LS".parse::<Group>().unwrap_err(),
4237 AttributeError::ParamNotFound {
4238 param: "Media identification tags".to_string(),
4239 attr: "group".to_string()
4240 }
4241 );
4242
4243 assert_eq!(
4245 "".parse::<Ssrc>().unwrap_err(),
4246 AttributeError::ParamNotFound {
4247 param: "Ssrc id".to_string(),
4248 attr: "ssrc".to_string()
4249 }
4250 );
4251 assert_eq!(
4252 "abc".parse::<Ssrc>().unwrap_err(),
4253 AttributeError::ParamNotFound {
4254 param: "Ssrc id".to_string(),
4255 attr: "ssrc".to_string()
4256 }
4257 );
4258
4259 let setup_err = "foo".parse::<Setup>().err().unwrap();
4261 assert!(matches!(setup_err, AttributeError::Other { .. }));
4262 assert_eq!(format!("{}", setup_err), "setup: Invalid Setup value foo");
4263
4264 assert_eq!(
4266 "".parse::<Crypto>().unwrap_err(),
4267 AttributeError::InvalidParamValue {
4268 param: "Tag".to_string(),
4269 val: "".to_string(),
4270 attr: "crypto".to_string()
4271 }
4272 );
4273 assert_eq!(
4274 "abc AES_CM_128_HMAC_SHA1_32 inline:key"
4275 .parse::<Crypto>()
4276 .unwrap_err(),
4277 AttributeError::InvalidParamValue {
4278 param: "Tag".to_string(),
4279 val: "abc".to_string(),
4280 attr: "crypto".to_string()
4281 }
4282 );
4283
4284 assert_eq!(
4286 "".parse::<RtcpFb>().unwrap_err(),
4287 AttributeError::InvalidParamValue {
4288 param: "Payload format".to_string(),
4289 val: "".to_string(),
4290 attr: "rtcp-fb".to_string()
4291 }
4292 );
4293 assert_eq!(
4294 "*".parse::<RtcpFb>().unwrap_err(),
4295 AttributeError::ParamNotFound {
4296 param: "Rtcp feedback value".to_string(),
4297 attr: "rtcp-fb".to_string()
4298 }
4299 );
4300 assert_eq!(
4301 "1 ack ccfb".parse::<RtcpFb>().unwrap_err(),
4302 AttributeError::InvalidParamValue {
4303 param: "Payload type of Congestion control feedback (ccfb)".to_string(),
4304 val: "1(expected wildcard (*))".to_string(),
4305 attr: "rtcp-fb".to_string()
4306 }
4307 );
4308
4309 let media = Media {
4311 media: "video".into(),
4312 port: 1234,
4313 num_ports: None,
4314 proto: "RTP/SAVPF".into(),
4315 fmt: "".into(),
4316 media_title: None,
4317 connections: vec![],
4318 bandwidths: vec![],
4319 key: None,
4320 attributes: vec![Attribute {
4321 attribute: "rtpmap".into(),
4322 value: None,
4323 }],
4324 };
4325 assert_eq!(
4326 media
4327 .attributes_typed::<RtpMap>()
4328 .collect::<Vec<Result<RtpMap, AttributeError>>>()
4329 .remove(0)
4330 .unwrap_err(),
4331 AttributeError::UnsupportedFormat {
4332 val: String::new(),
4333 attr: "rtpmap".to_string()
4334 }
4335 );
4336 }
4337
4338 #[test]
4339 #[allow(deprecated)]
4340 fn parse_rtcp_address() {
4341 let sdp = "v=0\r
4342o=alice 3203093520 3203093520 IN IP4 host.example.com\r
4343s=parse rtcp attribute address test\r
4344a=rtcp:5000 IN IP4 127.0.0.1\r
4345a=rtcp:5000 IN IP4 127.0.0.0/24\r
4346a=rtcp:5000 IN IP6 ::1\r
4347a=rtcp:5000 IN NONIP non-IP\r
4348a=rtcp:53020\r
4349";
4350
4351 let parsed = Session::parse(sdp.as_bytes()).unwrap();
4352 let mut rtcp_attr_iter = parsed.attributes_typed::<Rtcp>();
4353
4354 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4355 assert_eq!(rtcp.addrtype, Some(AddrType::Ip4));
4356 assert_eq!(rtcp.connection_address.as_deref(), Some("127.0.0.1"));
4357 assert_eq!(
4358 rtcp.try_parse_connection_ip_address().unwrap().unwrap(),
4359 IpAddr::V4(Ipv4Addr::LOCALHOST),
4360 );
4361
4362 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4363 assert_eq!(rtcp.addrtype, Some(AddrType::Ip4));
4364 assert_eq!(rtcp.connection_address.as_deref(), Some("127.0.0.0/24"));
4365 assert_eq!(
4368 rtcp.try_parse_connection_ip_address().unwrap().unwrap(),
4369 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 0)),
4370 );
4371 let address = rtcp.try_address().unwrap().unwrap();
4372 assert_eq!(address.ttl, Some(24));
4373 assert_eq!(address.count, None);
4374 assert!(!rtcp.is_multicast());
4375
4376 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4377 assert_eq!(rtcp.addrtype, Some(AddrType::Ip6));
4378 assert_eq!(rtcp.connection_address.as_deref(), Some("::1"));
4379 assert_eq!(
4380 rtcp.try_parse_connection_ip_address().unwrap().unwrap(),
4381 IpAddr::V6(Ipv6Addr::LOCALHOST),
4382 );
4383
4384 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4385 assert_eq!(rtcp.addrtype, Some(AddrType::Other("NONIP".to_string())));
4386 assert_eq!(rtcp.connection_address.as_deref(), Some("non-IP"));
4387 assert_eq!(
4388 rtcp.try_parse_connection_ip_address().unwrap().unwrap_err(),
4389 "non-IP",
4390 );
4391
4392 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4394 assert_eq!(rtcp.port, 53020);
4395 assert_eq!(rtcp.nettype, None);
4396 assert_eq!(rtcp.addrtype, None);
4397 assert_eq!(rtcp.connection_address, None);
4398 assert_eq!(rtcp.try_parse_connection_ip_address(), None);
4399 assert_eq!(rtcp.try_address(), None);
4400 assert!(!rtcp.is_multicast());
4401 assert_eq!(rtcp.to_string(), "53020");
4402 }
4403
4404 #[test]
4405 fn rtcp_multicast_address_helpers() {
4406 let rtcp = "5000 IN IP4 233.252.0.1/127/3".parse::<Rtcp>().unwrap();
4412 assert!(rtcp.is_multicast());
4413 assert_eq!(
4414 rtcp.try_address().unwrap().unwrap(),
4415 ConnectionAddress {
4416 ip: Ipv4Addr::new(233, 252, 0, 1).into(),
4417 ttl: Some(127),
4418 count: Some(3),
4419 }
4420 );
4421
4422 let mut rtcp = Rtcp::with_port(5000);
4425 rtcp.connection_address = Some("224.2.1.1/16".to_string());
4426 let address = rtcp.try_address().unwrap().unwrap();
4427 assert_eq!(address.ttl, Some(16));
4428 assert_eq!(address.count, None);
4429
4430 let mut rtcp = Rtcp::with_port(5000);
4431 rtcp.connection_address = Some("ff12::5/3".to_string());
4432 let address = rtcp.try_address().unwrap().unwrap();
4433 assert_eq!(address.ttl, None);
4434 assert_eq!(address.count, Some(3));
4435
4436 let rtcp = "5000 IN IP6 ff12::5/3".parse::<Rtcp>().unwrap();
4437 assert!(rtcp.is_ipv6());
4438 assert!(!rtcp.is_ipv4());
4439 assert_eq!(rtcp.try_is_multicast(), Some(Ok(true)));
4440
4441 let rtcp = Rtcp::with_port(5000);
4444 assert_eq!(rtcp.try_is_multicast(), None);
4445 assert!(!rtcp.is_multicast());
4446 assert!(!rtcp.is_ipv4());
4447 assert!(!rtcp.is_ipv6());
4448
4449 let rtcp = "5000 IN IP4 rtcp.example.com".parse::<Rtcp>().unwrap();
4451 assert!(!rtcp.is_multicast());
4452 assert!(!rtcp.is_ipv4());
4453 assert_eq!(
4454 rtcp.try_is_multicast(),
4455 Some(Err(ConnectionAddressError::InvalidAddress(
4456 "rtcp.example.com".to_string()
4457 )))
4458 );
4459 }
4460
4461 #[test]
4462 fn parse_rtcp_attribute_whitespace() {
4463 assert_eq!(
4467 "6000 IN IP4 127.0.0.1".parse::<Rtcp>().unwrap(),
4468 Rtcp::new(6000, NetType::In, AddrType::Ip4, "127.0.0.1")
4469 );
4470 assert_eq!(
4471 "6001 IN IP4 127.0.0.1 ".parse::<Rtcp>().unwrap(),
4472 Rtcp::new(6001, NetType::In, AddrType::Ip4, "127.0.0.1")
4473 );
4474 assert_eq!("53020 ".parse::<Rtcp>().unwrap(), Rtcp::with_port(53020));
4475
4476 assert!("6002 IN 127.0.0.1".parse::<Rtcp>().is_err());
4477 assert!("6003 IN IP4 127.0.0.1 extra".parse::<Rtcp>().is_err());
4478 }
4479
4480 #[test]
4481 fn parse_rtcp_nettype_and_addrtype_must_be_tokens() {
4482 assert_eq!(
4485 "6000 a<b IP4 127.0.0.1".parse::<Rtcp>().unwrap_err(),
4486 AttributeError::InvalidParamValue {
4487 param: "Network type".to_string(),
4488 val: "a<b".to_string(),
4489 attr: "rtcp".to_string(),
4490 }
4491 );
4492 assert_eq!(
4493 "6000 IN IP4/x 127.0.0.1".parse::<Rtcp>().unwrap_err(),
4494 AttributeError::InvalidParamValue {
4495 param: "Address type".to_string(),
4496 val: "IP4/x".to_string(),
4497 attr: "rtcp".to_string(),
4498 }
4499 );
4500 }
4501
4502 #[test]
4503 fn parse_rid_attribute() {
4504 let rid: Rid = "5 send pt=99,102;max-br=64000".parse().unwrap();
4506 assert_eq!(
4507 rid,
4508 Rid::with_params("5", RidDirection::Send, ["pt=99,102", "max-br=64000"])
4509 );
4510 assert_eq!(rid.to_string(), "5 send pt=99,102;max-br=64000");
4511
4512 let rid: Rid = "6 send pt=100,97,101,102".parse().unwrap();
4513 assert_eq!(
4514 rid,
4515 Rid::with_params("6", RidDirection::Send, ["pt=100,97,101,102"])
4516 );
4517
4518 let rid: Rid = "f recv max-br=19.5;apt=100".parse().unwrap();
4519 assert_eq!(
4520 rid,
4521 Rid::with_params("f", RidDirection::Recv, ["max-br=19.5", "apt=100"])
4522 );
4523 assert_eq!(rid.to_string(), "f recv max-br=19.5;apt=100");
4524
4525 let rid: Rid = "1 recv".parse().unwrap();
4527 assert_eq!(rid.direction, RidDirection::Recv);
4528 assert!(rid.params.is_empty());
4529 assert_eq!(rid.to_string(), "1 recv");
4530 assert_eq!(
4531 "2 send max-width".parse::<Rid>().unwrap(),
4532 Rid::with_params("2", RidDirection::Send, ["max-width"])
4533 );
4534
4535 assert_eq!(
4537 "2 SEND pt=99;".parse::<Rid>().unwrap(),
4538 Rid::with_params("2", RidDirection::Send, ["pt=99"])
4539 );
4540
4541 for invalid in [
4542 "",
4543 "1",
4544 " send",
4545 "1! send",
4548 "id:1 recv",
4549 "1 up",
4550 "1 send x y",
4551 "1 send pt=1;max br=2",
4552 ] {
4553 assert!(
4554 invalid.parse::<Rid>().is_err(),
4555 "expected error for {:?}",
4556 invalid
4557 );
4558 }
4559 }
4560
4561 #[test]
4562 fn parse_key_mgmt_attribute() {
4563 let key_mgmt: KeyMgmt = "mikey AAAAIklkAAAAdGV4dHRleHR0ZXh0".parse().unwrap();
4564 assert_eq!(
4565 key_mgmt,
4566 KeyMgmt::with_data(KeyMgmtMethod::Mikey, "AAAAIklkAAAAdGV4dHRleHR0ZXh0")
4567 );
4568 assert_eq!(key_mgmt.to_string(), "mikey AAAAIklkAAAAdGV4dHRleHR0ZXh0");
4569
4570 for method in ["rawurl", "MIKEY", "internal"] {
4573 let key_mgmt: KeyMgmt = format!("{} data", method).parse().unwrap();
4574 assert_eq!(key_mgmt.method, KeyMgmtMethod::Other(method.to_string()));
4575 assert_eq!(key_mgmt.data.as_deref(), Some("data"));
4576 assert_eq!(key_mgmt.to_string(), format!("{} data", method));
4577 }
4578
4579 let key_mgmt: KeyMgmt = "internal".parse().unwrap();
4581 assert_eq!(
4582 key_mgmt,
4583 KeyMgmt::new(KeyMgmtMethod::Other("internal".to_string()))
4584 );
4585 assert_eq!(key_mgmt.to_string(), "internal");
4586
4587 let key_mgmt: KeyMgmt = "mikey bar baz".parse().unwrap();
4589 assert_eq!(key_mgmt.data.as_deref(), Some("bar baz"));
4590 assert_eq!(key_mgmt.to_string(), "mikey bar baz");
4591
4592 assert!("".parse::<KeyMgmt>().is_err());
4593 assert!(" ".parse::<KeyMgmt>().is_err());
4594 assert!("mi!key data".parse::<KeyMgmt>().is_err());
4596 assert!("-mikey".parse::<KeyMgmt>().is_err());
4597 }
4598
4599 #[test]
4600 fn parse_remote_candidate_attribute() {
4601 let candidates: RemoteCandidates = "1 192.168.1.1 50000".parse().unwrap();
4602 assert_eq!(
4603 candidates,
4604 RemoteCandidates::new([RemoteCandidate::new(
4605 1,
4606 "192.168.1.1".parse::<IpAddr>().unwrap(),
4607 50000
4608 )])
4609 );
4610 assert_eq!(candidates.to_string(), "1 192.168.1.1 50000");
4611
4612 let candidates: RemoteCandidates = "1 192.0.2.3 45664 2 192.0.2.3 45665".parse().unwrap();
4614 assert_eq!(candidates.0.len(), 2);
4615 assert_eq!(candidates.0[1].component_id, 2);
4616 assert_eq!(
4617 candidates.to_string(),
4618 "1 192.0.2.3 45664 2 192.0.2.3 45665"
4619 );
4620
4621 let candidate: RemoteCandidate = "1 2001:db8::1 9".parse().unwrap();
4623 assert_eq!(candidate.address, "2001:db8::1".parse::<IpAddr>().unwrap());
4624 assert_eq!(candidate.to_string(), "1 2001:db8::1 9");
4625
4626 for invalid in [
4627 "",
4628 "1",
4629 "1 10.0.0.1",
4630 "x 10.0.0.1 50000",
4631 "1 x 50000",
4632 "1 example.com 50000",
4634 "1 10.0.0.1 70000",
4635 "1 10.0.0.1 50000 2",
4636 ] {
4637 assert!(
4638 invalid.parse::<RemoteCandidates>().is_err(),
4639 "expected error for {:?}",
4640 invalid
4641 );
4642 }
4643 }
4644
4645 #[test]
4646 fn parse_source_filter_attribute() {
4647 let filter: SourceFilter = "incl IN IP4 232.3.4.5 192.0.2.10".parse().unwrap();
4649 assert_eq!(filter.mode, SourceFilterMode::Incl);
4650 assert_eq!(filter.nettype, NetType::In);
4651 assert_eq!(filter.addrtype, SourceFilterAddrType::Ip4);
4652 assert_eq!(
4653 filter.destination,
4654 SourceFilterDestination::Address(SourceFilterAddress::IpAddr(
4655 "232.3.4.5".parse().unwrap()
4656 ))
4657 );
4658 assert_eq!(
4659 filter.sources,
4660 ["192.0.2.10".parse::<SourceFilterAddress>().unwrap()]
4661 );
4662 assert_eq!(filter.to_string(), "incl IN IP4 232.3.4.5 192.0.2.10");
4663
4664 let filter: SourceFilter = "excl IN IP4 192.0.2.11 192.0.2.10".parse().unwrap();
4666 assert_eq!(filter.mode, SourceFilterMode::Excl);
4667 assert_eq!(filter.to_string(), "excl IN IP4 192.0.2.11 192.0.2.10");
4668
4669 let filter: SourceFilter = "incl IN IP4 * 192.0.2.10".parse().unwrap();
4671 assert_eq!(filter.destination, SourceFilterDestination::Wildcard);
4672 assert_eq!(filter.to_string(), "incl IN IP4 * 192.0.2.10");
4673
4674 let filter: SourceFilter = "incl IN IP4 224.2.1.1 192.0.2.10 192.0.2.42"
4676 .parse()
4677 .unwrap();
4678 assert_eq!(filter.sources.len(), 2);
4679 assert_eq!(
4680 filter.to_string(),
4681 "incl IN IP4 224.2.1.1 192.0.2.10 192.0.2.42"
4682 );
4683
4684 let filter: SourceFilter = "incl IN IP6 ff0e::11a 2001:db8::1".parse().unwrap();
4686 assert_eq!(filter.addrtype, SourceFilterAddrType::Ip6);
4687 assert_eq!(
4688 filter.destination,
4689 SourceFilterDestination::Address(SourceFilterAddress::IpAddr(
4690 "ff0e::11a".parse().unwrap()
4691 ))
4692 );
4693 assert_eq!(filter.to_string(), "incl IN IP6 ff0e::11a 2001:db8::1");
4694
4695 let filter: SourceFilter = "incl IN * channel-1.example.com src-1.example.com"
4697 .parse()
4698 .unwrap();
4699 assert_eq!(filter.addrtype, SourceFilterAddrType::Any);
4700 assert_eq!(
4701 filter.destination,
4702 SourceFilterDestination::Address(SourceFilterAddress::FQDN(
4703 "channel-1.example.com".to_string()
4704 ))
4705 );
4706 assert_eq!(
4707 filter.sources,
4708 [SourceFilterAddress::FQDN("src-1.example.com".to_string())]
4709 );
4710 assert_eq!(
4711 filter.to_string(),
4712 "incl IN * channel-1.example.com src-1.example.com"
4713 );
4714
4715 let filter: SourceFilter = "EXCL IN ip6 ff0e::11a 2001:db8::1".parse().unwrap();
4717 assert_eq!(filter.mode, SourceFilterMode::Excl);
4718 assert_eq!(filter.addrtype, SourceFilterAddrType::Ip6);
4719
4720 for invalid in [
4721 "",
4722 "incl",
4723 "incl IN",
4724 "incl IN IP4",
4725 "incl IN IP4 232.3.4.5",
4726 "incl IN IP4 232.3.4.5/127 192.0.2.10",
4729 "incl IN * 232.3.4.5 192.0.2.10",
4731 "incl IN IP4 232.3.4.5 *",
4733 "unknown IN IP4 232.3.4.5 192.0.2.10",
4734 "incl IN(IP4 232.3.4.5 192.0.2.10",
4736 "incl IN IP8 232.3.4.5 192.0.2.10",
4737 ] {
4738 assert!(
4739 invalid.parse::<SourceFilter>().is_err(),
4740 "expected error for {:?}",
4741 invalid
4742 );
4743 }
4744 }
4745
4746 #[test]
4747 fn parse_mid_attribute() {
4748 let mid: Mid = "1".parse().unwrap();
4749 assert_eq!(mid, Mid("1".to_string()));
4750 assert_eq!(mid.to_string(), "1");
4751
4752 let attribute: Attribute = mid.clone().into();
4753 assert_eq!(attribute.attribute, "mid");
4754 assert_eq!(attribute.value.as_deref(), Some("1"));
4755
4756 let mid: Mid = "v2R1d1".parse().unwrap();
4758 assert_eq!(mid.to_string(), "v2R1d1");
4759
4760 for invalid in [
4761 "", " ", "1 2", "a:b",
4763 ] {
4764 assert!(
4765 invalid.parse::<Mid>().is_err(),
4766 "expected error for {:?}",
4767 invalid
4768 );
4769 }
4770 }
4771
4772 #[test]
4773 fn parse_rtcp_mux_attribute() {
4774 assert_eq!("".parse::<RtcpMux>().unwrap(), RtcpMux);
4775
4776 let attribute: Attribute = RtcpMux.into();
4777 assert_eq!(attribute.attribute, "rtcp-mux");
4778 assert_eq!(attribute.value, None);
4779
4780 assert!("1".parse::<RtcpMux>().is_err());
4782
4783 let sdp = "v=0\r
4784o=alice 3203093520 3203093520 IN IP4 host.example.com\r
4785s=Property attribute test\r
4786t=3203130148 3203137348\r
4787m=audio 49170 RTP/AVP 0\r
4788a=rtcp-mux\r
4789";
4790 let parsed = Session::parse(sdp.as_bytes()).unwrap();
4791 assert_eq!(
4792 parsed.medias[0].get_first_attribute_typed::<RtcpMux>(),
4793 Some(Ok(RtcpMux))
4794 );
4795 }
4796
4797 #[test]
4798 fn parse_rtcp_mux_only_attribute() {
4799 assert!(matches!("".parse::<RtcpMuxOnly>(), Ok(RtcpMuxOnly)));
4800
4801 let attribute: Attribute = RtcpMuxOnly.into();
4802 assert_eq!(attribute.attribute, "rtcp-mux-only");
4803 assert_eq!(attribute.value, None);
4804
4805 assert!("1".parse::<RtcpMuxOnly>().is_err());
4807 }
4808
4809 #[test]
4810 fn parse_rtcp_rsize_attribute() {
4811 assert!(matches!("".parse::<RtcpRsize>(), Ok(RtcpRsize)));
4812
4813 let attribute: Attribute = RtcpRsize.into();
4814 assert_eq!(attribute.attribute, "rtcp-rsize");
4815 assert_eq!(attribute.value, None);
4816
4817 assert!("1".parse::<RtcpRsize>().is_err());
4819 }
4820
4821 #[test]
4822 fn parse_ice_lite_attribute() {
4823 assert!(matches!("".parse::<IceLite>(), Ok(IceLite)));
4824
4825 let attribute: Attribute = IceLite.into();
4826 assert_eq!(attribute.attribute, "ice-lite");
4827 assert_eq!(attribute.value, None);
4828
4829 assert!("1".parse::<IceLite>().is_err());
4831 }
4832
4833 #[test]
4834 fn parse_ice_ufrag_attribute() {
4835 let ufrag: IceUfrag = "EpGh".parse().unwrap();
4836 assert_eq!(ufrag, IceUfrag("EpGh".to_string()));
4837 assert_eq!(ufrag.to_string(), "EpGh");
4838
4839 let attribute: Attribute = ufrag.into();
4840 assert_eq!(attribute.attribute, "ice-ufrag");
4841 assert_eq!(attribute.value.as_deref(), Some("EpGh"));
4842
4843 assert_eq!("a+Z/9".parse::<IceUfrag>().unwrap().to_string(), "a+Z/9");
4846
4847 assert!("a".repeat(256).parse::<IceUfrag>().is_ok());
4849
4850 let too_long = "a".repeat(257);
4851 for invalid in [
4852 "",
4853 "abc", too_long.as_str(), "Ep-Gh", "Ep Gh", "\u{30b0}pGh", ] {
4859 assert!(
4860 invalid.parse::<IceUfrag>().is_err(),
4861 "expected error for {:?}",
4862 invalid
4863 );
4864 }
4865 }
4866
4867 #[test]
4868 fn parse_ice_pwd_attribute() {
4869 let password = "aq1s2dq3w4er5ty6ui7p8o9i0u";
4870 let pwd: IcePwd = password.parse().unwrap();
4871 assert_eq!(pwd, IcePwd(password.to_string()));
4872 assert_eq!(pwd.to_string(), password);
4873
4874 let attribute: Attribute = pwd.into();
4875 assert_eq!(attribute.attribute, "ice-pwd");
4876 assert_eq!(attribute.value.as_deref(), Some(password));
4877
4878 let shortest = "a".repeat(22);
4880 assert_eq!(shortest.parse::<IcePwd>().unwrap().to_string(), shortest);
4881 assert!("a".repeat(256).parse::<IcePwd>().is_ok());
4882
4883 let too_long = "a".repeat(257);
4884 for invalid in [
4885 "",
4886 "a".repeat(21).as_str(), too_long.as_str(), "abcdefghijklmnopqr-stu", "\u{30b0}bcdefghijklmnopqrstuvw", ] {
4891 assert!(
4892 invalid.parse::<IcePwd>().is_err(),
4893 "expected error for {:?}",
4894 invalid
4895 );
4896 }
4897 }
4898
4899 #[test]
4900 fn parse_ice_options_attribute() {
4901 let options: IceOptions = "trickle".parse().unwrap();
4902 assert_eq!(options, IceOptions::new(["trickle"]));
4903 assert_eq!(options.to_string(), "trickle");
4904
4905 let options: IceOptions = "trickle renomination".parse().unwrap();
4907 assert_eq!(options, IceOptions::new(["trickle", "renomination"]));
4908 assert_eq!(options.to_string(), "trickle renomination");
4909
4910 let attribute: Attribute = options.into();
4911 assert_eq!(attribute.attribute, "ice-options");
4912 assert_eq!(attribute.value.as_deref(), Some("trickle renomination"));
4913
4914 for invalid in ["", " ", "tr-ickle"] {
4915 assert!(
4916 invalid.parse::<IceOptions>().is_err(),
4917 "expected error for {:?}",
4918 invalid
4919 );
4920 }
4921 }
4922
4923 #[test]
4924 fn parse_end_of_candidates_attribute() {
4925 assert!(matches!("".parse::<EndOfCandidates>(), Ok(EndOfCandidates)));
4926
4927 let attribute: Attribute = EndOfCandidates.into();
4928 assert_eq!(attribute.attribute, "end-of-candidates");
4929 assert_eq!(attribute.value, None);
4930
4931 assert!("1".parse::<EndOfCandidates>().is_err());
4933 }
4934
4935 #[test]
4936 fn parse_msid_attribute() {
4937 let msid: Msid = "examplefoo examplebar".parse().unwrap();
4939 assert_eq!(msid, Msid::new("examplefoo", Some("examplebar")));
4940 assert_eq!(msid.to_string(), "examplefoo examplebar");
4941
4942 let attribute: Attribute = msid.into();
4943 assert_eq!(attribute.attribute, "msid");
4944 assert_eq!(attribute.value.as_deref(), Some("examplefoo examplebar"));
4945
4946 let msid: Msid = "-".parse().unwrap();
4949 assert_eq!(msid, Msid::new("-", None::<&str>));
4950 assert_eq!(msid.to_string(), "-");
4951
4952 let too_long = "a".repeat(65);
4953 for invalid in [
4954 "",
4955 too_long.as_str(), "foo \u{fffd}", "foo bar baz", ] {
4959 assert!(
4960 invalid.parse::<Msid>().is_err(),
4961 "expected error for {:?}",
4962 invalid
4963 );
4964 }
4965 }
4966
4967 #[test]
4968 fn parse_sctp_port_attribute() {
4969 let port: SctpPort = "5000".parse().unwrap();
4971 assert_eq!(port, SctpPort(5000));
4972 assert_eq!(port.to_string(), "5000");
4973
4974 let attribute: Attribute = port.into();
4975 assert_eq!(attribute.attribute, "sctp-port");
4976 assert_eq!(attribute.value.as_deref(), Some("5000"));
4977
4978 assert_eq!("0".parse::<SctpPort>().unwrap(), SctpPort(0));
4980 assert_eq!("65535".parse::<SctpPort>().unwrap(), SctpPort(65535));
4981
4982 for invalid in [
4983 "", "65536", "99999", "05000", "500000", "abc", "50a",
4988 ] {
4989 assert!(
4990 invalid.parse::<SctpPort>().is_err(),
4991 "expected error for {:?}",
4992 invalid
4993 );
4994 }
4995 }
4996
4997 #[test]
4998 fn parse_max_message_size_attribute() {
4999 let size: MaxMessageSize = "100000".parse().unwrap();
5001 assert_eq!(size, MaxMessageSize(100000));
5002 assert_eq!(size.to_string(), "100000");
5003
5004 let attribute: Attribute = size.into();
5005 assert_eq!(attribute.attribute, "max-message-size");
5006 assert_eq!(attribute.value.as_deref(), Some("100000"));
5007
5008 assert_eq!("0".parse::<MaxMessageSize>().unwrap(), MaxMessageSize(0));
5010
5011 let too_large = "1".repeat(21);
5012 for invalid in [
5013 "",
5014 "01", "1_000",
5016 "abc",
5017 "10a",
5018 too_large.as_str(), ] {
5020 assert!(
5021 invalid.parse::<MaxMessageSize>().is_err(),
5022 "expected error for {:?}",
5023 invalid
5024 );
5025 }
5026 }
5027
5028 #[test]
5029 fn parse_sctpmap_attribute() {
5030 let map: SctpMap = "5000 webrtc-datachannel 256".parse().unwrap();
5032 assert_eq!(map, SctpMap::new(5000, "webrtc-datachannel", Some(256)));
5033 assert_eq!(map.to_string(), "5000 webrtc-datachannel 256");
5034
5035 let attribute: Attribute = map.into();
5036 assert_eq!(attribute.attribute, "sctpmap");
5037 assert_eq!(
5038 attribute.value.as_deref(),
5039 Some("5000 webrtc-datachannel 256")
5040 );
5041
5042 let map: SctpMap = "9 webrtc-datachannel".parse().unwrap();
5044 assert_eq!(map.streams, None);
5045 assert_eq!(map.to_string(), "9 webrtc-datachannel");
5046 assert_eq!("0 app 0".parse::<SctpMap>().unwrap().streams, Some(0));
5047
5048 for invalid in [
5049 "",
5050 "5000", "65536 app", "05000 app", "app app", "5000 app 70000", "5000 a:pp", "5000 app 256 extra", ] {
5058 assert!(
5059 invalid.parse::<SctpMap>().is_err(),
5060 "expected error for {:?}",
5061 invalid
5062 );
5063 }
5064 }
5065}