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)]
1205#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1206pub struct Group {
1207 pub semantics: GroupSemantics,
1208 pub mid_tags: Vec<String>,
1209}
1210
1211impl Group {
1212 pub fn new(semantics: GroupSemantics) -> Self {
1213 Group {
1214 semantics,
1215 mid_tags: vec![],
1216 }
1217 }
1218
1219 pub fn add_mid_tag(&mut self, mid_tag: impl ToString) {
1220 self.mid_tags.push(mid_tag.to_string())
1221 }
1222
1223 pub fn add_mid_tags(&mut self, mid_tags: impl IntoIterator<Item = impl ToString>) {
1224 self.mid_tags
1225 .extend(mid_tags.into_iter().map(|i| i.to_string()))
1226 }
1227}
1228
1229impl FromStr for Group {
1230 type Err = AttributeError;
1231
1232 fn from_str(s: &str) -> Result<Self, Self::Err> {
1233 let mut i = s.split_ascii_whitespace();
1237
1238 let Some(semantics) = i.next() else {
1239 return Err(AttributeError::ParamNotFound {
1240 param: "Semantics".to_string(),
1241 attr: <Self as TypedAttribute>::NAME.to_string(),
1242 });
1243 };
1244
1245 let semantics = GroupSemantics::new(semantics);
1246
1247 let mut mid_tags = vec![];
1248 for mid in i {
1249 mid_tags.push(mid.to_string());
1250 }
1251
1252 Ok(Self {
1253 semantics,
1254 mid_tags,
1255 })
1256 }
1257}
1258
1259impl Display for Group {
1260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1261 f.write_str(self.semantics.as_str())?;
1262 for m in &self.mid_tags {
1263 f.write_char(' ')?;
1264 f.write_str(m)?;
1265 }
1266 Ok(())
1267 }
1268}
1269
1270impl TypedAttribute for Group {
1271 const NAME: &'static str = "group";
1272}
1273
1274#[derive(Debug, Clone, PartialEq, Eq)]
1278#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1279pub enum Setup {
1280 Active,
1282 Passive,
1284 ActPass,
1286 HoldConn,
1288}
1289
1290impl FromStr for Setup {
1291 type Err = AttributeError;
1292
1293 fn from_str(s: &str) -> Result<Self, Self::Err> {
1294 if "active".eq_ignore_ascii_case(s) {
1295 Ok(Setup::Active)
1296 } else if "passive".eq_ignore_ascii_case(s) {
1297 Ok(Setup::Passive)
1298 } else if "actpass".eq_ignore_ascii_case(s) {
1299 Ok(Setup::ActPass)
1300 } else if "holdconn".eq_ignore_ascii_case(s) {
1301 Ok(Setup::HoldConn)
1302 } else {
1303 Err(AttributeError::Other {
1304 error: format!("Invalid Setup value {s}"),
1305 attr: <Self as TypedAttribute>::NAME.to_string(),
1306 })
1307 }
1308 }
1309}
1310
1311impl Display for Setup {
1312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1313 let s = match self {
1314 Setup::Active => "active",
1315 Setup::Passive => "passive",
1316 Setup::ActPass => "actpass",
1317 Setup::HoldConn => "holdconn",
1318 };
1319 f.write_str(s)
1320 }
1321}
1322
1323impl TypedAttribute for Setup {
1324 const NAME: &'static str = "setup";
1325}
1326
1327#[derive(Debug, Clone, PartialEq, Eq)]
1331#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1332pub struct Ssrc {
1333 pub ssrc_id: u32,
1334 pub attribute: SsrcAttribute,
1335 pub value: Option<String>,
1336}
1337
1338impl Ssrc {
1339 pub fn new(ssrc_id: u32, attribute: SsrcAttribute) -> Self {
1340 Ssrc {
1341 ssrc_id,
1342 attribute,
1343 value: None,
1344 }
1345 }
1346
1347 pub fn with_typed_attribute(ssrc_id: u32, attribute: impl TypedAttribute) -> Self {
1348 let value = attribute.to_string();
1349 Ssrc {
1350 ssrc_id,
1351 attribute: SsrcAttribute::from(attribute),
1352 value: Some(value),
1353 }
1354 }
1355
1356 pub fn with_value(ssrc_id: u32, attribute: SsrcAttribute, value: impl ToString) -> Self {
1357 Ssrc {
1358 ssrc_id,
1359 attribute,
1360 value: Some(value.to_string()),
1361 }
1362 }
1363
1364 pub fn set_value(&mut self, value: impl ToString) {
1365 self.value = Some(value.to_string());
1366 }
1367
1368 pub fn get_typed<T: TypedAttribute>(&self) -> Result<T, AttributeError> {
1376 if self.attribute.as_str() != T::NAME {
1377 return Err(AttributeError::Other {
1378 error: format!("Attribute type mismatch (requested {})", T::NAME),
1379 attr: self.attribute.as_str().to_string(),
1380 });
1381 }
1382
1383 T::from_str(self.value.as_deref().unwrap_or_default())
1386 }
1387}
1388
1389impl FromStr for Ssrc {
1390 type Err = AttributeError;
1391
1392 fn from_str(s: &str) -> Result<Self, Self::Err> {
1393 let Some((ssrc_id_str, rest)) = s.split_once(' ') else {
1394 return Err(AttributeError::ParamNotFound {
1395 param: "Ssrc id".to_string(),
1396 attr: <Self as TypedAttribute>::NAME.to_string(),
1397 });
1398 };
1399
1400 let Ok(ssrc_id) = ssrc_id_str.parse::<u32>() else {
1401 return Err(AttributeError::InvalidParamValue {
1402 param: "Ssrc id".to_string(),
1403 val: ssrc_id_str.to_string(),
1404 attr: <Self as TypedAttribute>::NAME.to_string(),
1405 });
1406 };
1407
1408 let (attr, value) = if let Some((attr_str, value)) = rest.split_once(':') {
1409 (attr_str, Some(value.to_string()))
1410 } else {
1411 (rest, None)
1412 };
1413
1414 Ok(Self {
1415 ssrc_id,
1416 attribute: SsrcAttribute::new(attr),
1417 value,
1418 })
1419 }
1420}
1421
1422impl Display for Ssrc {
1423 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1424 use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
1425
1426 let attr_str = match &self.attribute {
1427 SsrcAttribute::Cname => "cname",
1428 SsrcAttribute::PreviousSsrc => "previous-ssrc",
1429 SsrcAttribute::Fmtp => <Fmtp as TypedAttribute>::NAME,
1430 SsrcAttribute::Rtcp => <Rtcp as TypedAttribute>::NAME,
1431 SsrcAttribute::ReferenceClock => <ReferenceClock as TypedAttribute>::NAME,
1432 SsrcAttribute::MediaClockSource => <MediaClockSource as TypedAttribute>::NAME,
1433 SsrcAttribute::Other(other) => other.as_str(),
1434 };
1435 write!(f, "{} {attr_str}", self.ssrc_id)?;
1436
1437 if let Some(value) = &self.value {
1438 f.write_char(':')?;
1439 f.write_str(value)?;
1440 }
1441
1442 Ok(())
1443 }
1444}
1445
1446impl TypedAttribute for Ssrc {
1447 const NAME: &'static str = "ssrc";
1448}
1449
1450#[derive(Debug, Clone, PartialEq, Eq)]
1454#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1455pub struct SsrcGroup {
1456 pub semantics: GroupSemantics,
1457 pub ssrc_ids: Vec<u32>,
1458}
1459
1460impl SsrcGroup {
1461 pub fn new(semantics: GroupSemantics) -> Self {
1462 SsrcGroup {
1463 semantics,
1464 ssrc_ids: vec![],
1465 }
1466 }
1467
1468 pub fn add_ssrc_id(&mut self, ssrc_id: u32) {
1469 self.ssrc_ids.push(ssrc_id)
1470 }
1471
1472 pub fn add_ssrc_ids(&mut self, ssrc_ids: impl IntoIterator<Item = u32>) {
1473 self.ssrc_ids.extend(ssrc_ids)
1474 }
1475}
1476
1477impl FromStr for SsrcGroup {
1478 type Err = AttributeError;
1479
1480 fn from_str(s: &str) -> Result<Self, Self::Err> {
1481 let mut i = s.split_ascii_whitespace();
1484
1485 let Some(semantics) = i.next() else {
1486 return Err(AttributeError::ParamNotFound {
1487 param: "Semantics".to_string(),
1488 attr: <Self as TypedAttribute>::NAME.to_string(),
1489 });
1490 };
1491
1492 let semantics = if "FEC".eq_ignore_ascii_case(semantics) {
1493 GroupSemantics::FEC
1494 } else if "FID".eq_ignore_ascii_case(semantics) {
1495 GroupSemantics::FID
1496 } else {
1497 GroupSemantics::Other(semantics.to_string())
1501 };
1502
1503 let mut ssrc_ids = vec![];
1504 for ssrc_id in i {
1505 let Ok(ssrc_id) = ssrc_id.parse::<u32>() else {
1506 return Err(AttributeError::InvalidParamValue {
1507 param: "Ssrc id".to_string(),
1508 val: ssrc_id.to_string(),
1509 attr: <Self as TypedAttribute>::NAME.to_string(),
1510 });
1511 };
1512 ssrc_ids.push(ssrc_id);
1513 }
1514
1515 if ssrc_ids.is_empty() {
1516 return Err(AttributeError::ParamNotFound {
1517 param: "ssrc_id".to_string(),
1518 attr: <Self as TypedAttribute>::NAME.to_string(),
1519 });
1520 }
1521
1522 Ok(Self {
1523 semantics,
1524 ssrc_ids,
1525 })
1526 }
1527}
1528
1529impl Display for SsrcGroup {
1530 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1531 f.write_str(self.semantics.as_str())?;
1534 for ssrc_id in &self.ssrc_ids {
1535 f.write_char(' ')?;
1536 write!(f, "{ssrc_id}")?;
1537 }
1538
1539 Ok(())
1540 }
1541}
1542
1543impl TypedAttribute for SsrcGroup {
1544 const NAME: &'static str = "ssrc-group";
1545}
1546
1547#[derive(Debug, PartialEq, Eq, Clone)]
1551#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1552pub struct SrtpKeyParam {
1553 pub key_and_salt: String,
1555 pub lifetime: Option<u64>,
1559 pub mki_and_length: Option<(u32, u32)>,
1561}
1562
1563impl SrtpKeyParam {
1564 pub fn new(key_and_salt: impl ToString) -> Self {
1565 SrtpKeyParam {
1566 key_and_salt: key_and_salt.to_string(),
1567 lifetime: None,
1568 mki_and_length: None,
1569 }
1570 }
1571
1572 pub fn with_lifetime(key_and_salt: impl ToString, lifetime: u64) -> Self {
1573 SrtpKeyParam {
1574 key_and_salt: key_and_salt.to_string(),
1575 lifetime: Some(lifetime),
1576 mki_and_length: None,
1577 }
1578 }
1579
1580 pub fn with_lifetime_and_mki_and_length(
1581 key_and_salt: impl ToString,
1582 lifetime: u64,
1583 mki: u32,
1584 length: u32,
1585 ) -> Self {
1586 SrtpKeyParam {
1587 key_and_salt: key_and_salt.to_string(),
1588 lifetime: Some(lifetime),
1589 mki_and_length: Some((mki, length)),
1590 }
1591 }
1592
1593 pub fn set_lifetime(&mut self, lifetime: u64) {
1594 self.lifetime = Some(lifetime);
1595 }
1596
1597 pub fn set_mki_and_length(&mut self, mki: u32, length: u32) {
1598 self.mki_and_length = Some((mki, length));
1599 }
1600}
1601
1602fn parse_decimal_no_leading_zeroes(s: &str) -> Option<u64> {
1606 if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
1607 return None;
1608 }
1609 if s.len() > 1 && s.starts_with('0') {
1610 return None;
1611 }
1612 s.parse().ok()
1613}
1614
1615impl FromStr for SrtpKeyParam {
1616 type Err = AttributeError;
1617 fn from_str(key_param: &str) -> Result<Self, Self::Err> {
1618 let mut k = key_param.split('|');
1619
1620 let Some(key_and_salt_with_method) = k.next() else {
1621 return Err(AttributeError::ParamNotFound {
1622 param: "Srtp Key and Salt".to_string(),
1623 attr: Crypto::NAME.to_string(),
1624 });
1625 };
1626
1627 let key_and_salt = if key_and_salt_with_method
1628 .get(..7)
1629 .is_some_and(|p| p.eq_ignore_ascii_case("inline:"))
1630 {
1631 &key_and_salt_with_method[7..]
1632 } else {
1633 return Err(AttributeError::InvalidParamValue {
1634 param: "Srtp Key and Salt".to_string(),
1635 val: key_and_salt_with_method.to_string(),
1636 attr: Crypto::NAME.to_string(),
1637 });
1638 };
1639
1640 let (lifetime, mki_and_length) = if let Some(next_param) = k.next() {
1641 match next_param.split_once(':') {
1642 Some(mki_and_length) => {
1643 let Ok(mki) = mki_and_length.0.parse::<u32>() else {
1645 return Err(AttributeError::InvalidParamValue {
1646 param: "MKI".to_string(),
1647 val: next_param.to_string(),
1648 attr: Crypto::NAME.to_string(),
1649 });
1650 };
1651
1652 let Ok(len) = mki_and_length.1.parse::<u32>() else {
1653 return Err(AttributeError::InvalidParamValue {
1654 param: "Length".to_string(),
1655 val: next_param.to_string(),
1656 attr: Crypto::NAME.to_string(),
1657 });
1658 };
1659 (None, Some((mki, len)))
1660 }
1661 None => {
1662 const MAX_LIFETIME: u64 = 1 << 48;
1666 let lifetime = match next_param.strip_prefix("2^") {
1667 Some(exp) => {
1668 let Some(exp) = parse_decimal_no_leading_zeroes(exp)
1669 .and_then(|exp| u32::try_from(exp).ok())
1670 else {
1671 return Err(AttributeError::InvalidParamValue {
1672 param: "Lifetime".to_string(),
1673 val: next_param.to_string(),
1674 attr: Crypto::NAME.to_string(),
1675 });
1676 };
1677 let Some(lifetime) = 1u64.checked_shl(exp) else {
1678 return Err(AttributeError::InvalidParamValue {
1679 param: "Lifetime".to_string(),
1680 val: next_param.to_string(),
1681 attr: Crypto::NAME.to_string(),
1682 });
1683 };
1684 if lifetime > MAX_LIFETIME {
1686 return Err(AttributeError::InvalidParamValue {
1687 param: "Lifetime".to_string(),
1688 val: next_param.to_string(),
1689 attr: Crypto::NAME.to_string(),
1690 });
1691 }
1692 Some(lifetime)
1693 }
1694 None => {
1695 let Some(lifetime) = parse_decimal_no_leading_zeroes(next_param) else {
1696 return Err(AttributeError::InvalidParamValue {
1697 param: "Lifetime".to_string(),
1698 val: next_param.to_string(),
1699 attr: Crypto::NAME.to_string(),
1700 });
1701 };
1702 if lifetime == 0 || lifetime > MAX_LIFETIME {
1703 return Err(AttributeError::InvalidParamValue {
1704 param: "Lifetime".to_string(),
1705 val: next_param.to_string(),
1706 attr: Crypto::NAME.to_string(),
1707 });
1708 }
1709 Some(lifetime)
1710 }
1711 };
1712
1713 let mki_and_length = if let Some(m) = k.next() {
1715 if let Some(p) = m.split_once(':') {
1716 let Ok(mki) = p.0.parse::<u32>() else {
1717 return Err(AttributeError::InvalidParamValue {
1718 param: "MKI".to_string(),
1719 val: m.to_string(),
1720 attr: Crypto::NAME.to_string(),
1721 });
1722 };
1723
1724 let Ok(len) = p.1.parse::<u32>() else {
1725 return Err(AttributeError::InvalidParamValue {
1726 param: "Length".to_string(),
1727 val: m.to_string(),
1728 attr: Crypto::NAME.to_string(),
1729 });
1730 };
1731 Some((mki, len))
1732 } else {
1733 return Err(AttributeError::ParamNotFound {
1734 param: "MKI and Length".to_string(),
1735 attr: Crypto::NAME.to_string(),
1736 });
1737 }
1738 } else {
1739 None
1740 };
1741
1742 (lifetime, mki_and_length)
1743 }
1744 }
1745 } else {
1746 (None, None)
1747 };
1748
1749 if let Some((_, len)) = mki_and_length {
1750 if !(1..=128).contains(&len) {
1751 return Err(AttributeError::InvalidParamValue {
1752 param: "MKI length".to_string(),
1753 val: len.to_string(),
1754 attr: Crypto::NAME.to_string(),
1755 });
1756 }
1757 }
1758
1759 if let Some(unexpected) = k.next() {
1760 return Err(AttributeError::UnexpectedTrailingItem {
1761 val: unexpected.to_string(),
1762 attr: Crypto::NAME.to_string(),
1763 });
1764 }
1765
1766 Ok(Self {
1767 key_and_salt: key_and_salt.to_string(),
1768 lifetime,
1769 mki_and_length,
1770 })
1771 }
1772}
1773
1774impl Display for SrtpKeyParam {
1775 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1776 write!(f, "inline:{}", self.key_and_salt)?;
1777 if let Some(lifetime) = self.lifetime {
1778 if lifetime.is_power_of_two() {
1779 write!(f, "|2^{}", lifetime.trailing_zeros())?;
1780 } else {
1781 write!(f, "|{lifetime}")?;
1782 }
1783 }
1784 if let Some((mki, length)) = self.mki_and_length {
1785 write!(f, "|{mki}:{length}")?;
1786 }
1787 Ok(())
1788 }
1789}
1790
1791#[derive(Debug, PartialEq, Eq, Clone)]
1795#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1796pub struct Crypto {
1797 pub tag: u32,
1798 pub crypto_suite: CryptoSuite,
1799 pub key_params: Vec<SrtpKeyParam>,
1800 pub session_params: Vec<SrtpSessionParam>,
1801}
1802
1803impl Crypto {
1804 pub fn new(tag: u32, crypto_suite: CryptoSuite) -> Self {
1805 Crypto {
1806 tag,
1807 crypto_suite,
1808 key_params: vec![],
1809 session_params: vec![],
1810 }
1811 }
1812
1813 pub fn add_key_param(&mut self, key_param: SrtpKeyParam) {
1814 self.key_params.push(key_param)
1815 }
1816
1817 pub fn add_key_params(&mut self, key_params: impl IntoIterator<Item = SrtpKeyParam>) {
1818 self.key_params.extend(key_params)
1819 }
1820
1821 pub fn add_session_param(&mut self, session_param: SrtpSessionParam) {
1822 self.session_params.push(session_param)
1823 }
1824
1825 pub fn add_session_params(
1826 &mut self,
1827 session_params: impl IntoIterator<Item = SrtpSessionParam>,
1828 ) {
1829 self.session_params.extend(session_params)
1830 }
1831}
1832
1833impl FromStr for Crypto {
1834 type Err = AttributeError;
1835
1836 fn from_str(s: &str) -> Result<Self, Self::Err> {
1837 let mut i = s.split(' ');
1838
1839 let Some(tag) = i.next() else {
1840 return Err(AttributeError::ParamNotFound {
1841 param: "Tag".to_string(),
1842 attr: <Self as TypedAttribute>::NAME.to_string(),
1843 });
1844 };
1845
1846 let Ok(tag) = tag.parse::<u32>() else {
1847 return Err(AttributeError::InvalidParamValue {
1848 param: "Tag".to_string(),
1849 val: tag.to_string(),
1850 attr: <Self as TypedAttribute>::NAME.to_string(),
1851 });
1852 };
1853
1854 let Some(crypto_suite) = i.next() else {
1855 return Err(AttributeError::ParamNotFound {
1856 param: "CryptoSuite".to_string(),
1857 attr: <Self as TypedAttribute>::NAME.to_string(),
1858 });
1859 };
1860
1861 let crypto_suite = CryptoSuite::new(crypto_suite);
1862
1863 let Some(key_params_str) = i.next() else {
1864 return Err(AttributeError::ParamNotFound {
1865 param: "Key params".to_string(),
1866 attr: <Self as TypedAttribute>::NAME.to_string(),
1867 });
1868 };
1869
1870 let mut key_params: Vec<SrtpKeyParam> = Vec::new();
1871
1872 for key_param in key_params_str.split(';') {
1873 let key_param = SrtpKeyParam::from_str(key_param)?;
1874 key_params.push(key_param);
1875 }
1876
1877 let mut session_params: Vec<SrtpSessionParam> = Vec::new();
1878 for s in &mut i {
1879 let param = if s.get(..4).is_some_and(|p| p.eq_ignore_ascii_case("KDR=")) {
1880 let kdr_val = &s[4..];
1881 let Ok(kdr_val) = kdr_val.parse::<u8>() else {
1882 return Err(AttributeError::InvalidParamValue {
1883 param: "KDR".to_string(),
1884 val: kdr_val.to_string(),
1885 attr: <Self as TypedAttribute>::NAME.to_string(),
1886 });
1887 };
1888
1889 if !(0..=24).contains(&kdr_val) {
1894 return Err(AttributeError::InvalidParamValue {
1895 param: "KDR".to_string(),
1896 val: format!("{kdr_val}(expected range 0..24)"),
1897 attr: <Self as TypedAttribute>::NAME.to_string(),
1898 });
1899 }
1900 SrtpSessionParam::Kdr(kdr_val)
1901 } else if s.eq_ignore_ascii_case("UNENCRYPTED_SRTCP") {
1902 SrtpSessionParam::UnencryptedSrtcp
1903 } else if s.eq_ignore_ascii_case("UNENCRYPTED_SRTP") {
1904 SrtpSessionParam::UnencryptedSrtp
1905 } else if s.eq_ignore_ascii_case("UNAUTHENTICATED_SRTP") {
1906 SrtpSessionParam::UnauthenticatedSrtp
1907 } else if s
1908 .get(..10)
1909 .is_some_and(|p| p.eq_ignore_ascii_case("FEC_ORDER="))
1910 {
1911 let fec_ord = &s[10..];
1912 if fec_ord.eq_ignore_ascii_case("FEC_SRTP") {
1913 SrtpSessionParam::FecOrder(FecOrder::FecSrtp)
1914 } else if fec_ord.eq_ignore_ascii_case("SRTP_FEC") {
1915 SrtpSessionParam::FecOrder(FecOrder::SrtpFec)
1916 } else {
1917 return Err(AttributeError::InvalidParamValue {
1918 param: "FEC order".to_string(),
1919 val: s.to_string(),
1920 attr: <Self as TypedAttribute>::NAME.to_string(),
1921 });
1922 }
1923 } else if s
1924 .get(..8)
1925 .is_some_and(|p| p.eq_ignore_ascii_case("FEC_KEY="))
1926 {
1927 let key_params_str = &s[8..];
1928 let mut key_params: Vec<SrtpKeyParam> = Vec::new();
1929
1930 for key_param in key_params_str.split(';') {
1931 let key_param = SrtpKeyParam::from_str(key_param)?;
1932 key_params.push(key_param);
1933 }
1934 SrtpSessionParam::FecKey(key_params)
1935 } else if s.get(..4).is_some_and(|p| p.eq_ignore_ascii_case("WSH=")) {
1936 let wsh_val = &s[4..];
1937 let Ok(wsh_val) = wsh_val.parse::<u8>() else {
1938 return Err(AttributeError::InvalidParamValue {
1939 param: "WSH".to_string(),
1940 val: wsh_val.to_string(),
1941 attr: <Self as TypedAttribute>::NAME.to_string(),
1942 });
1943 };
1944
1945 if wsh_val < 64 {
1946 return Err(AttributeError::InvalidParamValue {
1947 param: "WSH".to_string(),
1948 val: wsh_val.to_string(),
1949 attr: <Self as TypedAttribute>::NAME.to_string(),
1950 });
1951 }
1952 SrtpSessionParam::Wsh(wsh_val)
1953 } else {
1954 SrtpSessionParam::Extension(s.to_string())
1956 };
1957 session_params.push(param);
1958 }
1959
1960 Ok(Self {
1961 tag,
1962 key_params,
1963 crypto_suite,
1964 session_params,
1965 })
1966 }
1967}
1968
1969impl Display for Crypto {
1970 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1971 write!(f, "{} {}", self.tag, self.crypto_suite.as_str())?;
1972
1973 for (i, key_param) in self.key_params.iter().enumerate() {
1974 if i == 0 {
1975 f.write_char(' ')?;
1976 } else {
1977 f.write_char(';')?;
1978 }
1979
1980 write!(f, "{}", key_param)?;
1981 }
1982
1983 for session_param in &self.session_params {
1984 match session_param {
1985 SrtpSessionParam::Kdr(kdr) => write!(f, " KDR={kdr}")?,
1986 SrtpSessionParam::UnencryptedSrtp => write!(f, " UNENCRYPTED_SRTP")?,
1987 SrtpSessionParam::UnencryptedSrtcp => write!(f, " UNENCRYPTED_SRTCP")?,
1988 SrtpSessionParam::UnauthenticatedSrtp => write!(f, " UNAUTHENTICATED_SRTP")?,
1989 SrtpSessionParam::FecOrder(fec_order) => {
1990 let order = match fec_order {
1991 FecOrder::FecSrtp => "FEC_SRTP",
1992 FecOrder::SrtpFec => "SRTP_FEC",
1993 };
1994 write!(f, " FEC_ORDER={order}")?;
1995 }
1996 SrtpSessionParam::FecKey(srtp_key_params) => {
1997 write!(f, " FEC_KEY")?;
1998 for (i, key_param) in srtp_key_params.iter().enumerate() {
1999 if i == 0 {
2000 f.write_char('=')?;
2001 } else {
2002 f.write_char(';')?;
2003 }
2004
2005 write!(f, "{}", key_param)?;
2006 }
2007 }
2008 SrtpSessionParam::Wsh(wsh) => write!(f, " WSH={wsh}")?,
2009 SrtpSessionParam::Extension(extn) => {
2010 f.write_char(' ')?;
2011 f.write_str(extn)?;
2012 }
2013 }
2014 }
2015 Ok(())
2016 }
2017}
2018
2019impl TypedAttribute for Crypto {
2020 const NAME: &'static str = "crypto";
2021}
2022
2023#[derive(Debug, PartialEq, Eq, Clone)]
2027#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2028pub struct Candidate {
2029 pub foundation: String,
2032 pub component_id: u32,
2035 pub transport: String,
2037 pub priority: u64,
2039 pub address: CandidateAddress,
2042 pub port: u16,
2044 pub typ: CandidateType,
2046 pub rel_addr: Option<IpAddr>,
2049 pub rel_port: Option<u16>,
2052 pub extensions: Vec<(String, String)>,
2054}
2055
2056impl Candidate {
2057 pub fn new(
2058 foundation: impl ToString,
2059 component_id: u32,
2060 transport: impl ToString,
2061 priority: u64,
2062 address: CandidateAddress,
2063 port: u16,
2064 typ: CandidateType,
2065 ) -> Self {
2066 Candidate {
2067 foundation: foundation.to_string(),
2068 component_id,
2069 transport: transport.to_string(),
2070 priority,
2071 address,
2072 port,
2073 typ,
2074 rel_addr: None,
2075 rel_port: None,
2076 extensions: vec![],
2077 }
2078 }
2079
2080 #[allow(clippy::too_many_arguments)]
2081 pub fn with_rel_addr(
2082 foundation: impl ToString,
2083 component_id: u32,
2084 transport: impl ToString,
2085 priority: u64,
2086 address: CandidateAddress,
2087 port: u16,
2088 typ: CandidateType,
2089 rel_addr: impl Into<IpAddr>,
2090 ) -> Self {
2091 Candidate {
2092 foundation: foundation.to_string(),
2093 component_id,
2094 transport: transport.to_string(),
2095 priority,
2096 address,
2097 port,
2098 typ,
2099 rel_addr: Some(rel_addr.into()),
2100 rel_port: None,
2101 extensions: vec![],
2102 }
2103 }
2104
2105 #[allow(clippy::too_many_arguments)]
2106 pub fn with_rel_addr_and_port(
2107 foundation: impl ToString,
2108 component_id: u32,
2109 transport: impl ToString,
2110 priority: u64,
2111 address: CandidateAddress,
2112 port: u16,
2113 typ: CandidateType,
2114 rel_addr: impl Into<IpAddr>,
2115 rel_port: u16,
2116 ) -> Self {
2117 Candidate {
2118 foundation: foundation.to_string(),
2119 component_id,
2120 transport: transport.to_string(),
2121 priority,
2122 address,
2123 port,
2124 typ,
2125 rel_addr: Some(rel_addr.into()),
2126 rel_port: Some(rel_port),
2127 extensions: vec![],
2128 }
2129 }
2130
2131 pub fn set_rel_addr(&mut self, rel_addr: impl Into<IpAddr>) {
2132 self.rel_addr = Some(rel_addr.into());
2133 }
2134
2135 pub fn set_rel_port(&mut self, rel_port: u16) {
2136 self.rel_port = Some(rel_port);
2137 }
2138
2139 pub fn add_extension(&mut self, name: impl ToString, value: impl ToString) {
2140 self.extensions.push((name.to_string(), value.to_string()))
2141 }
2142}
2143
2144impl FromStr for Candidate {
2145 type Err = AttributeError;
2146
2147 fn from_str(s: &str) -> Result<Self, Self::Err> {
2148 let mut i = s.split(' ');
2149
2150 let Some(foundation) = i.next() else {
2151 return Err(AttributeError::ParamNotFound {
2152 param: "Foundation".to_string(),
2153 attr: <Self as TypedAttribute>::NAME.to_string(),
2154 });
2155 };
2156
2157 let Some(comp_id) = i.next() else {
2158 return Err(AttributeError::ParamNotFound {
2159 param: "Component id".to_string(),
2160 attr: <Self as TypedAttribute>::NAME.to_string(),
2161 });
2162 };
2163
2164 let Ok(comp_id) = comp_id.parse::<u32>() else {
2165 return Err(AttributeError::InvalidParamValue {
2166 param: "Component id".to_string(),
2167 val: comp_id.to_string(),
2168 attr: <Self as TypedAttribute>::NAME.to_string(),
2169 });
2170 };
2171
2172 let Some(transport) = i.next() else {
2173 return Err(AttributeError::ParamNotFound {
2174 param: "Transport".to_string(),
2175 attr: <Self as TypedAttribute>::NAME.to_string(),
2176 });
2177 };
2178
2179 let Some(priority) = i.next() else {
2180 return Err(AttributeError::ParamNotFound {
2181 param: "Priority".to_string(),
2182 attr: <Self as TypedAttribute>::NAME.to_string(),
2183 });
2184 };
2185
2186 let Ok(priority) = priority.parse::<u64>() else {
2187 return Err(AttributeError::InvalidParamValue {
2188 param: "Priority".to_string(),
2189 val: priority.to_string(),
2190 attr: <Self as TypedAttribute>::NAME.to_string(),
2191 });
2192 };
2193
2194 let Some(address) = i.next() else {
2195 return Err(AttributeError::ParamNotFound {
2196 param: "Address".to_string(),
2197 attr: <Self as TypedAttribute>::NAME.to_string(),
2198 });
2199 };
2200
2201 let address = match address.parse::<IpAddr>() {
2202 Ok(a) => CandidateAddress::IpAddr(a),
2203 Err(_) => CandidateAddress::FQDN(address.to_string()),
2204 };
2205
2206 let Some(port) = i.next() else {
2207 return Err(AttributeError::ParamNotFound {
2208 param: "Port".to_string(),
2209 attr: <Self as TypedAttribute>::NAME.to_string(),
2210 });
2211 };
2212
2213 let Ok(port) = port.parse::<u16>() else {
2214 return Err(AttributeError::InvalidParamValue {
2215 param: "Port".to_string(),
2216 val: port.to_string(),
2217 attr: <Self as TypedAttribute>::NAME.to_string(),
2218 });
2219 };
2220
2221 let Some(typ_str) = i.next() else {
2222 return Err(AttributeError::ParamNotFound {
2223 param: "'typ' string".to_string(),
2224 attr: <Self as TypedAttribute>::NAME.to_string(),
2225 });
2226 };
2227
2228 if !typ_str.eq_ignore_ascii_case("typ") {
2229 return Err(AttributeError::ParamNotFound {
2230 param: "'typ' string".to_string(),
2231 attr: <Self as TypedAttribute>::NAME.to_string(),
2232 });
2233 }
2234
2235 let Some(cand_type) = i.next() else {
2236 return Err(AttributeError::ParamNotFound {
2237 param: "Candidate type".to_string(),
2238 attr: <Self as TypedAttribute>::NAME.to_string(),
2239 });
2240 };
2241
2242 let cand_type = CandidateType::new(cand_type);
2243
2244 let mut rel_addr: Option<IpAddr> = None;
2245 let mut rel_port: Option<u16> = None;
2246 let mut exts: Vec<(String, String)> = Vec::new();
2247
2248 while let Some(key) = i.next() {
2249 if key.eq_ignore_ascii_case("raddr") {
2250 let Some(raddr) = i.next() else {
2251 return Err(AttributeError::ParamNotFound {
2252 param: "Relative address".to_string(),
2253 attr: <Self as TypedAttribute>::NAME.to_string(),
2254 });
2255 };
2256
2257 if let Ok(raddr) = raddr.parse::<IpAddr>() {
2258 rel_addr = Some(raddr);
2259 } else {
2260 return Err(AttributeError::InvalidParamValue {
2261 param: "Relative address".to_string(),
2262 val: raddr.to_string(),
2263 attr: <Self as TypedAttribute>::NAME.to_string(),
2264 });
2265 };
2266 } else if key.eq_ignore_ascii_case("rport") {
2267 let Some(rport) = i.next() else {
2268 return Err(AttributeError::ParamNotFound {
2269 param: "Relative port".to_string(),
2270 attr: <Self as TypedAttribute>::NAME.to_string(),
2271 });
2272 };
2273
2274 if let Ok(rport) = rport.parse::<u16>() {
2275 rel_port = Some(rport);
2276 } else {
2277 return Err(AttributeError::InvalidParamValue {
2278 param: "Relative port".to_string(),
2279 val: rport.to_string(),
2280 attr: <Self as TypedAttribute>::NAME.to_string(),
2281 });
2282 }
2283 } else {
2284 let Some(val) = i.next() else {
2285 return Err(AttributeError::Other {
2286 error: format!("No val for the extension {key}"),
2287 attr: <Self as TypedAttribute>::NAME.to_string(),
2288 });
2289 };
2290
2291 exts.push((key.to_string(), val.to_string()));
2292 }
2293 }
2294
2295 Ok(Self {
2296 foundation: foundation.to_string(),
2297 component_id: comp_id,
2298 transport: transport.to_string(),
2299 priority,
2300 address,
2301 port,
2302 typ: cand_type,
2303 rel_addr,
2304 rel_port,
2305 extensions: exts,
2306 })
2307 }
2308}
2309
2310impl Display for Candidate {
2311 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2312 let candidate_addr = match &self.address {
2313 CandidateAddress::IpAddr(a) => a.to_string(),
2314 CandidateAddress::FQDN(d) => d.clone(),
2315 };
2316 write!(
2317 f,
2318 "{} {} {} {} {} {} typ {}",
2319 self.foundation,
2320 self.component_id,
2321 self.transport,
2322 self.priority,
2323 candidate_addr,
2324 self.port,
2325 self.typ.as_str(),
2326 )?;
2327 if let Some(rel_addr) = self.rel_addr {
2328 write!(f, " raddr {rel_addr}")?;
2329 }
2330 if let Some(rel_port) = self.rel_port {
2331 write!(f, " rport {rel_port}")?;
2332 }
2333 for (key, val) in &self.extensions {
2334 write!(f, " {key} {val}")?;
2335 }
2336 Ok(())
2337 }
2338}
2339
2340impl TypedAttribute for Candidate {
2341 const NAME: &'static str = "candidate";
2342}
2343
2344#[derive(Debug, Clone, PartialEq, Eq)]
2353#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2354pub struct Rid {
2355 pub id: String,
2357 pub direction: RidDirection,
2359 pub params: Vec<String>,
2361}
2362
2363impl Rid {
2364 pub fn new(id: impl ToString, direction: RidDirection) -> Self {
2365 Rid {
2366 id: id.to_string(),
2367 direction,
2368 params: Vec::new(),
2369 }
2370 }
2371
2372 pub fn add_param(&mut self, param: impl ToString) {
2373 self.params.push(param.to_string());
2374 }
2375
2376 pub fn with_params(
2377 id: impl ToString,
2378 direction: RidDirection,
2379 params: impl IntoIterator<Item = impl ToString>,
2380 ) -> Self {
2381 Rid {
2382 id: id.to_string(),
2383 direction,
2384 params: params.into_iter().map(|p| p.to_string()).collect(),
2385 }
2386 }
2387}
2388
2389impl FromStr for Rid {
2390 type Err = AttributeError;
2391
2392 fn from_str(s: &str) -> Result<Self, Self::Err> {
2393 let attr = <Self as TypedAttribute>::NAME.to_string();
2394 let (id, rest) = s
2395 .split_once(' ')
2396 .ok_or_else(|| AttributeError::ParamNotFound {
2397 param: "Direction".to_string(),
2398 attr: attr.clone(),
2399 })?;
2400
2401 let id = id.trim();
2402 if id.is_empty() {
2403 return Err(AttributeError::ParamNotFound {
2404 param: "ID".to_string(),
2405 attr: attr.clone(),
2406 });
2407 }
2408
2409 if !id
2412 .bytes()
2413 .all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
2414 {
2415 return Err(AttributeError::InvalidParamValue {
2416 param: "ID".to_string(),
2417 val: id.to_string(),
2418 attr,
2419 });
2420 }
2421
2422 let (direction_str, params_str) = match rest.trim().split_once(' ') {
2425 Some((d, p)) => (d, p),
2426 None => (rest.trim(), ""),
2427 };
2428
2429 let direction = direction_str
2430 .parse()
2431 .map_err(|_| AttributeError::InvalidParamValue {
2432 param: "Direction".to_string(),
2433 val: direction_str.to_string(),
2434 attr: attr.clone(),
2435 })?;
2436
2437 let params = if params_str.trim().is_empty() {
2438 Vec::new()
2439 } else {
2440 params_str
2441 .trim()
2442 .split(';')
2443 .map(|p| p.trim())
2444 .filter(|p| !p.is_empty())
2445 .map(|p| {
2446 if p.contains(char::is_whitespace) {
2447 Err(AttributeError::InvalidParamValue {
2448 param: "Parameter".to_string(),
2449 val: p.to_string(),
2450 attr: attr.clone(),
2451 })
2452 } else {
2453 Ok(p.to_string())
2454 }
2455 })
2456 .collect::<Result<Vec<_>, _>>()?
2457 };
2458
2459 Ok(Rid {
2460 id: id.to_string(),
2461 direction,
2462 params,
2463 })
2464 }
2465}
2466
2467impl Display for Rid {
2468 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2469 write!(f, "{} {}", self.id, self.direction)?;
2470 if !self.params.is_empty() {
2471 write!(f, " {}", self.params.join(";"))?;
2472 }
2473 Ok(())
2474 }
2475}
2476
2477impl TypedAttribute for Rid {
2478 const NAME: &'static str = "rid";
2479}
2480
2481#[derive(Debug, Clone, PartialEq, Eq)]
2488#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2489pub enum KeyMgmtMethod {
2490 Mikey,
2492 Other(String),
2494}
2495
2496impl From<&str> for KeyMgmtMethod {
2497 fn from(s: &str) -> Self {
2498 match s {
2499 "mikey" => KeyMgmtMethod::Mikey,
2500 other => KeyMgmtMethod::Other(other.to_string()),
2501 }
2502 }
2503}
2504
2505impl std::str::FromStr for KeyMgmtMethod {
2506 type Err = ();
2508
2509 fn from_str(s: &str) -> Result<Self, Self::Err> {
2510 Ok(s.into())
2511 }
2512}
2513
2514impl Display for KeyMgmtMethod {
2515 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2516 match self {
2517 KeyMgmtMethod::Mikey => f.write_str("mikey"),
2518 KeyMgmtMethod::Other(other) => f.write_str(other),
2519 }
2520 }
2521}
2522
2523#[derive(Debug, Clone, PartialEq, Eq)]
2532#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2533pub struct KeyMgmt {
2534 pub method: KeyMgmtMethod,
2536 pub data: Option<String>,
2538}
2539
2540impl KeyMgmt {
2541 pub fn new(method: KeyMgmtMethod) -> Self {
2542 KeyMgmt { method, data: None }
2543 }
2544
2545 pub fn with_data(method: KeyMgmtMethod, data: impl ToString) -> Self {
2546 KeyMgmt {
2547 method,
2548 data: Some(data.to_string()),
2549 }
2550 }
2551}
2552
2553impl FromStr for KeyMgmt {
2554 type Err = AttributeError;
2555
2556 fn from_str(s: &str) -> Result<Self, Self::Err> {
2557 let s = s.trim();
2558 if s.is_empty() {
2559 return Err(AttributeError::ParamNotFound {
2560 param: "Method".to_string(),
2561 attr: <Self as TypedAttribute>::NAME.to_string(),
2562 });
2563 }
2564
2565 let (method, data) = match s.split_once(char::is_whitespace) {
2566 Some((method, data)) => (method, Some(data.trim().to_string())),
2567 None => (s, None),
2568 };
2569
2570 if !method.bytes().all(|b| b.is_ascii_alphanumeric()) {
2572 return Err(AttributeError::InvalidParamValue {
2573 param: "Method".to_string(),
2574 val: method.to_string(),
2575 attr: <Self as TypedAttribute>::NAME.to_string(),
2576 });
2577 }
2578
2579 Ok(KeyMgmt {
2580 method: method.into(),
2581 data,
2582 })
2583 }
2584}
2585
2586impl Display for KeyMgmt {
2587 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2588 match &self.data {
2589 Some(data) => write!(f, "{} {}", self.method, data),
2590 None => write!(f, "{}", self.method),
2591 }
2592 }
2593}
2594
2595impl TypedAttribute for KeyMgmt {
2596 const NAME: &'static str = "key-mgmt";
2597}
2598
2599#[derive(Debug, Clone, PartialEq, Eq)]
2609#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2610pub struct RemoteCandidate {
2611 pub component_id: u32,
2613 pub address: IpAddr,
2615 pub port: u16,
2617}
2618
2619impl RemoteCandidate {
2620 pub fn new(component_id: u32, address: impl Into<IpAddr>, port: u16) -> Self {
2621 RemoteCandidate {
2622 component_id,
2623 address: address.into(),
2624 port,
2625 }
2626 }
2627}
2628
2629impl FromStr for RemoteCandidate {
2630 type Err = AttributeError;
2631
2632 fn from_str(s: &str) -> Result<Self, Self::Err> {
2633 let attr = <RemoteCandidates as TypedAttribute>::NAME.to_string();
2634 let mut parts = s.split_ascii_whitespace();
2635
2636 let component_id_token = parts.next().ok_or_else(|| AttributeError::ParamNotFound {
2637 param: "Component ID".to_string(),
2638 attr: attr.clone(),
2639 })?;
2640 let component_id =
2641 component_id_token
2642 .parse()
2643 .map_err(|_| AttributeError::InvalidParamValue {
2644 param: "Component ID".to_string(),
2645 val: component_id_token.to_string(),
2646 attr: attr.clone(),
2647 })?;
2648
2649 let address_token = parts.next().ok_or_else(|| AttributeError::ParamNotFound {
2650 param: "Address".to_string(),
2651 attr: attr.clone(),
2652 })?;
2653 let address = address_token
2654 .parse()
2655 .map_err(|_| AttributeError::InvalidParamValue {
2656 param: "Address".to_string(),
2657 val: address_token.to_string(),
2658 attr: attr.clone(),
2659 })?;
2660
2661 let port_token = parts.next().ok_or_else(|| AttributeError::ParamNotFound {
2662 param: "Port".to_string(),
2663 attr: attr.clone(),
2664 })?;
2665 let port = port_token
2666 .parse()
2667 .map_err(|_| AttributeError::InvalidParamValue {
2668 param: "Port".to_string(),
2669 val: port_token.to_string(),
2670 attr: attr.clone(),
2671 })?;
2672
2673 if let Some(val) = parts.next() {
2674 return Err(AttributeError::UnexpectedTrailingItem {
2675 val: val.to_string(),
2676 attr,
2677 });
2678 }
2679
2680 Ok(RemoteCandidate {
2681 component_id,
2682 address,
2683 port,
2684 })
2685 }
2686}
2687
2688impl Display for RemoteCandidate {
2689 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2690 write!(f, "{} {} {}", self.component_id, self.address, self.port)
2691 }
2692}
2693
2694#[derive(Debug, Clone, PartialEq, Eq)]
2705#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2706pub struct RemoteCandidates(pub Vec<RemoteCandidate>);
2707
2708impl RemoteCandidates {
2709 pub fn new(candidates: impl IntoIterator<Item = RemoteCandidate>) -> Self {
2710 RemoteCandidates(candidates.into_iter().collect())
2711 }
2712}
2713
2714impl FromStr for RemoteCandidates {
2715 type Err = AttributeError;
2716
2717 fn from_str(s: &str) -> Result<Self, Self::Err> {
2718 let attr = <Self as TypedAttribute>::NAME.to_string();
2719 let mut tokens = s.split_ascii_whitespace();
2720 let mut candidates = Vec::new();
2721
2722 loop {
2724 let triple: Vec<&str> = tokens.by_ref().take(3).collect();
2725 match triple.len() {
2726 0 => break,
2727 1..=2 => {
2728 return Err(AttributeError::UnexpectedTrailingItem {
2729 val: triple.join(" "),
2730 attr,
2731 })
2732 }
2733 _ => candidates.push(triple.join(" ").parse()?),
2734 }
2735 }
2736
2737 if candidates.is_empty() {
2738 return Err(AttributeError::ParamNotFound {
2739 param: "Candidate".to_string(),
2740 attr,
2741 });
2742 }
2743
2744 Ok(RemoteCandidates(candidates))
2745 }
2746}
2747
2748impl Display for RemoteCandidates {
2749 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2750 for (i, candidate) in self.0.iter().enumerate() {
2751 if i > 0 {
2752 f.write_char(' ')?;
2753 }
2754 write!(f, "{}", candidate)?;
2755 }
2756 Ok(())
2757 }
2758}
2759
2760impl TypedAttribute for RemoteCandidates {
2761 const NAME: &'static str = "remote-candidates";
2762}
2763
2764#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2768#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2769pub enum SourceFilterMode {
2770 Incl,
2772 Excl,
2774}
2775
2776impl SourceFilterMode {
2777 pub fn as_str(&self) -> &'static str {
2778 match self {
2779 SourceFilterMode::Incl => "incl",
2780 SourceFilterMode::Excl => "excl",
2781 }
2782 }
2783}
2784
2785impl FromStr for SourceFilterMode {
2786 type Err = ParseEnumError;
2787
2788 fn from_str(s: &str) -> Result<Self, Self::Err> {
2789 if "incl".eq_ignore_ascii_case(s) {
2790 Ok(SourceFilterMode::Incl)
2791 } else if "excl".eq_ignore_ascii_case(s) {
2792 Ok(SourceFilterMode::Excl)
2793 } else {
2794 Err(ParseEnumError::Invalid(s.to_string()))
2795 }
2796 }
2797}
2798
2799impl Display for SourceFilterMode {
2800 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2801 f.write_str(self.as_str())
2802 }
2803}
2804
2805#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2812#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2813pub enum SourceFilterAddrType {
2814 Any,
2816 Ip4,
2818 Ip6,
2820}
2821
2822impl SourceFilterAddrType {
2823 pub fn as_str(&self) -> &'static str {
2824 match self {
2825 SourceFilterAddrType::Any => "*",
2826 SourceFilterAddrType::Ip4 => "IP4",
2827 SourceFilterAddrType::Ip6 => "IP6",
2828 }
2829 }
2830}
2831
2832impl FromStr for SourceFilterAddrType {
2833 type Err = ParseEnumError;
2834
2835 fn from_str(s: &str) -> Result<Self, Self::Err> {
2836 match s {
2837 "*" => Ok(SourceFilterAddrType::Any),
2838 "IP4" => Ok(SourceFilterAddrType::Ip4),
2839 "IP6" => Ok(SourceFilterAddrType::Ip6),
2840 _ => Err(ParseEnumError::Invalid(s.to_string())),
2841 }
2842 }
2843}
2844
2845impl Display for SourceFilterAddrType {
2846 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2847 f.write_str(self.as_str())
2848 }
2849}
2850
2851#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2858#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2859pub enum SourceFilterAddress {
2860 IpAddr(IpAddr),
2862 FQDN(String),
2864}
2865
2866impl From<&str> for SourceFilterAddress {
2867 fn from(s: &str) -> Self {
2868 match s.parse::<IpAddr>() {
2869 Ok(address) => SourceFilterAddress::IpAddr(address),
2870 Err(_) => SourceFilterAddress::FQDN(s.to_string()),
2871 }
2872 }
2873}
2874
2875impl FromStr for SourceFilterAddress {
2876 type Err = ();
2878
2879 fn from_str(s: &str) -> Result<Self, Self::Err> {
2880 Ok(s.into())
2881 }
2882}
2883
2884impl Display for SourceFilterAddress {
2885 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2886 match self {
2887 SourceFilterAddress::IpAddr(address) => write!(f, "{}", address),
2888 SourceFilterAddress::FQDN(domain) => f.write_str(domain),
2889 }
2890 }
2891}
2892
2893#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2898#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2899pub enum SourceFilterDestination {
2900 Wildcard,
2902 Address(SourceFilterAddress),
2904}
2905
2906impl FromStr for SourceFilterDestination {
2907 type Err = ();
2909
2910 fn from_str(s: &str) -> Result<Self, Self::Err> {
2911 if s == "*" {
2912 Ok(SourceFilterDestination::Wildcard)
2913 } else {
2914 Ok(SourceFilterDestination::Address(s.into()))
2915 }
2916 }
2917}
2918
2919impl Display for SourceFilterDestination {
2920 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2921 match self {
2922 SourceFilterDestination::Wildcard => f.write_str("*"),
2923 SourceFilterDestination::Address(address) => write!(f, "{}", address),
2924 }
2925 }
2926}
2927
2928#[derive(Debug, Clone, PartialEq, Eq)]
2941#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2942pub struct SourceFilter {
2943 pub mode: SourceFilterMode,
2945 pub nettype: NetType,
2947 pub addrtype: SourceFilterAddrType,
2949 pub destination: SourceFilterDestination,
2951 pub sources: Vec<SourceFilterAddress>,
2953}
2954
2955impl FromStr for SourceFilter {
2956 type Err = AttributeError;
2957
2958 fn from_str(s: &str) -> Result<Self, Self::Err> {
2959 let attr = <Self as TypedAttribute>::NAME.to_string();
2960
2961 let mut i = s.split(' ').filter(|part| !part.is_empty());
2964
2965 let Some(mode) = i.next() else {
2966 return Err(AttributeError::ParamNotFound {
2967 param: "Filter mode".to_string(),
2968 attr,
2969 });
2970 };
2971 let mode = mode
2972 .parse()
2973 .map_err(|_| AttributeError::InvalidParamValue {
2974 param: "Filter mode".to_string(),
2975 val: mode.to_string(),
2976 attr: attr.clone(),
2977 })?;
2978
2979 let Some(nettype) = i.next() else {
2980 return Err(AttributeError::ParamNotFound {
2981 param: "Network type".to_string(),
2982 attr,
2983 });
2984 };
2985 if !is_token(nettype.as_bytes()) {
2987 return Err(AttributeError::InvalidParamValue {
2988 param: "Network type".to_string(),
2989 val: nettype.to_string(),
2990 attr: attr.clone(),
2991 });
2992 }
2993 let nettype = NetType::from_str(nettype).expect("infallible");
2994
2995 let Some(addrtype) = i.next() else {
2996 return Err(AttributeError::ParamNotFound {
2997 param: "Address types".to_string(),
2998 attr,
2999 });
3000 };
3001 let addrtype = addrtype
3002 .parse()
3003 .map_err(|_| AttributeError::InvalidParamValue {
3004 param: "Address types".to_string(),
3005 val: addrtype.to_string(),
3006 attr: attr.clone(),
3007 })?;
3008
3009 let Some(destination) = i.next() else {
3010 return Err(AttributeError::ParamNotFound {
3011 param: "Destination address".to_string(),
3012 attr,
3013 });
3014 };
3015
3016 if destination.contains('/') {
3019 return Err(AttributeError::InvalidParamValue {
3020 param: "Destination address".to_string(),
3021 val: destination.to_string(),
3022 attr,
3023 });
3024 }
3025 let destination = SourceFilterDestination::from_str(destination).expect("infallible");
3026 if addrtype == SourceFilterAddrType::Any
3029 && matches!(
3030 destination,
3031 SourceFilterDestination::Address(SourceFilterAddress::IpAddr(_))
3032 )
3033 {
3034 return Err(AttributeError::InvalidParamValue {
3035 param: "Destination address".to_string(),
3036 val: destination.to_string(),
3037 attr,
3038 });
3039 }
3040
3041 let sources: Vec<&str> = i.collect();
3042 if sources.is_empty() {
3043 return Err(AttributeError::ParamNotFound {
3044 param: "Source list".to_string(),
3045 attr,
3046 });
3047 }
3048 if let Some(source) = sources.iter().find(|source| source == &&"*") {
3050 return Err(AttributeError::InvalidParamValue {
3051 param: "Source address".to_string(),
3052 val: source.to_string(),
3053 attr,
3054 });
3055 }
3056 let sources = sources.into_iter().map(SourceFilterAddress::from).collect();
3057
3058 Ok(SourceFilter {
3059 mode,
3060 nettype,
3061 addrtype,
3062 destination,
3063 sources,
3064 })
3065 }
3066}
3067
3068impl Display for SourceFilter {
3069 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3070 write!(
3071 f,
3072 "{} {} {} {}",
3073 self.mode, self.nettype, self.addrtype, self.destination
3074 )?;
3075 for source in &self.sources {
3076 write!(f, " {}", source)?;
3077 }
3078 Ok(())
3079 }
3080}
3081
3082impl TypedAttribute for SourceFilter {
3083 const NAME: &'static str = "source-filter";
3084}
3085
3086#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3094#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3095pub struct Mid(pub String);
3096
3097impl FromStr for Mid {
3098 type Err = AttributeError;
3099
3100 fn from_str(s: &str) -> Result<Self, Self::Err> {
3101 if !is_token(s.as_bytes()) {
3102 return Err(AttributeError::InvalidParamValue {
3103 param: "Identification tag".to_string(),
3104 val: s.to_string(),
3105 attr: <Self as TypedAttribute>::NAME.to_string(),
3106 });
3107 }
3108
3109 Ok(Mid(s.to_string()))
3110 }
3111}
3112
3113impl Display for Mid {
3114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3115 f.write_str(&self.0)
3116 }
3117}
3118
3119impl TypedAttribute for Mid {
3120 const NAME: &'static str = "mid";
3121}
3122
3123#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3131#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3132pub struct RtcpMux;
3133
3134impl FromStr for RtcpMux {
3135 type Err = AttributeError;
3136
3137 fn from_str(s: &str) -> Result<Self, Self::Err> {
3138 if !s.is_empty() {
3139 return Err(AttributeError::UnsupportedFormat {
3140 val: s.to_string(),
3141 attr: <Self as TypedAttribute>::NAME.to_string(),
3142 });
3143 }
3144
3145 Ok(RtcpMux)
3146 }
3147}
3148
3149impl Display for RtcpMux {
3150 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3151 Ok(())
3152 }
3153}
3154
3155impl TypedAttribute for RtcpMux {
3156 const NAME: &'static str = "rtcp-mux";
3157}
3158
3159#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3169pub struct RtcpMuxOnly;
3170
3171impl FromStr for RtcpMuxOnly {
3172 type Err = AttributeError;
3173
3174 fn from_str(s: &str) -> Result<Self, Self::Err> {
3175 if !s.is_empty() {
3176 return Err(AttributeError::UnsupportedFormat {
3177 val: s.to_string(),
3178 attr: <Self as TypedAttribute>::NAME.to_string(),
3179 });
3180 }
3181
3182 Ok(RtcpMuxOnly)
3183 }
3184}
3185
3186impl Display for RtcpMuxOnly {
3187 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3188 Ok(())
3189 }
3190}
3191
3192impl TypedAttribute for RtcpMuxOnly {
3193 const NAME: &'static str = "rtcp-mux-only";
3194}
3195
3196#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3204#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3205pub struct RtcpRsize;
3206
3207impl FromStr for RtcpRsize {
3208 type Err = AttributeError;
3209
3210 fn from_str(s: &str) -> Result<Self, Self::Err> {
3211 if !s.is_empty() {
3212 return Err(AttributeError::UnsupportedFormat {
3213 val: s.to_string(),
3214 attr: <Self as TypedAttribute>::NAME.to_string(),
3215 });
3216 }
3217
3218 Ok(RtcpRsize)
3219 }
3220}
3221
3222impl Display for RtcpRsize {
3223 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3224 Ok(())
3225 }
3226}
3227
3228impl TypedAttribute for RtcpRsize {
3229 const NAME: &'static str = "rtcp-rsize";
3230}
3231
3232#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3240#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3241pub struct IceLite;
3242
3243impl FromStr for IceLite {
3244 type Err = AttributeError;
3245
3246 fn from_str(s: &str) -> Result<Self, Self::Err> {
3247 if !s.is_empty() {
3248 return Err(AttributeError::UnsupportedFormat {
3249 val: s.to_string(),
3250 attr: <Self as TypedAttribute>::NAME.to_string(),
3251 });
3252 }
3253
3254 Ok(IceLite)
3255 }
3256}
3257
3258impl Display for IceLite {
3259 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3260 Ok(())
3261 }
3262}
3263
3264impl TypedAttribute for IceLite {
3265 const NAME: &'static str = "ice-lite";
3266}
3267
3268fn is_ice_char(c: char) -> bool {
3270 matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '/')
3271}
3272
3273#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3282pub struct IceUfrag(pub String);
3283
3284impl FromStr for IceUfrag {
3285 type Err = AttributeError;
3286
3287 fn from_str(s: &str) -> Result<Self, Self::Err> {
3288 if !(4..=256).contains(&s.len()) || !s.chars().all(is_ice_char) {
3290 return Err(AttributeError::InvalidParamValue {
3291 param: "ICE username fragment".to_string(),
3292 val: s.to_string(),
3293 attr: <Self as TypedAttribute>::NAME.to_string(),
3294 });
3295 }
3296
3297 Ok(IceUfrag(s.to_string()))
3298 }
3299}
3300
3301impl Display for IceUfrag {
3302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3303 f.write_str(&self.0)
3304 }
3305}
3306
3307impl TypedAttribute for IceUfrag {
3308 const NAME: &'static str = "ice-ufrag";
3309}
3310
3311#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3319#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3320pub struct IcePwd(pub String);
3321
3322impl FromStr for IcePwd {
3323 type Err = AttributeError;
3324
3325 fn from_str(s: &str) -> Result<Self, Self::Err> {
3326 if !(22..=256).contains(&s.len()) || !s.chars().all(is_ice_char) {
3328 return Err(AttributeError::InvalidParamValue {
3329 param: "ICE password".to_string(),
3330 val: s.to_string(),
3331 attr: <Self as TypedAttribute>::NAME.to_string(),
3332 });
3333 }
3334
3335 Ok(IcePwd(s.to_string()))
3336 }
3337}
3338
3339impl Display for IcePwd {
3340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3341 f.write_str(&self.0)
3342 }
3343}
3344
3345impl TypedAttribute for IcePwd {
3346 const NAME: &'static str = "ice-pwd";
3347}
3348
3349#[derive(Debug, Clone, PartialEq, Eq)]
3356#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3357pub struct IceOptions(pub Vec<String>);
3358
3359impl IceOptions {
3360 pub fn new(tags: impl IntoIterator<Item = impl ToString>) -> Self {
3361 IceOptions(tags.into_iter().map(|tag| tag.to_string()).collect())
3362 }
3363}
3364
3365impl FromStr for IceOptions {
3366 type Err = AttributeError;
3367
3368 fn from_str(s: &str) -> Result<Self, Self::Err> {
3369 let attr = <Self as TypedAttribute>::NAME.to_string();
3370 let mut tags = Vec::new();
3371
3372 for tag in s.split_ascii_whitespace() {
3373 if !tag.chars().all(is_ice_char) {
3374 return Err(AttributeError::InvalidParamValue {
3375 param: "ICE option tag".to_string(),
3376 val: tag.to_string(),
3377 attr,
3378 });
3379 }
3380
3381 tags.push(tag.to_string());
3382 }
3383
3384 if tags.is_empty() {
3385 return Err(AttributeError::ParamNotFound {
3386 param: "ICE option tags".to_string(),
3387 attr,
3388 });
3389 }
3390
3391 Ok(IceOptions(tags))
3392 }
3393}
3394
3395impl Display for IceOptions {
3396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3397 for (i, tag) in self.0.iter().enumerate() {
3398 if i > 0 {
3399 f.write_char(' ')?;
3400 }
3401 f.write_str(tag)?;
3402 }
3403 Ok(())
3404 }
3405}
3406
3407impl TypedAttribute for IceOptions {
3408 const NAME: &'static str = "ice-options";
3409}
3410
3411#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
3419#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3420pub struct EndOfCandidates;
3421
3422impl FromStr for EndOfCandidates {
3423 type Err = AttributeError;
3424
3425 fn from_str(s: &str) -> Result<Self, Self::Err> {
3426 if !s.is_empty() {
3427 return Err(AttributeError::UnsupportedFormat {
3428 val: s.to_string(),
3429 attr: <Self as TypedAttribute>::NAME.to_string(),
3430 });
3431 }
3432
3433 Ok(EndOfCandidates)
3434 }
3435}
3436
3437impl Display for EndOfCandidates {
3438 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3439 Ok(())
3440 }
3441}
3442
3443impl TypedAttribute for EndOfCandidates {
3444 const NAME: &'static str = "end-of-candidates";
3445}
3446
3447#[derive(Debug, Clone, PartialEq, Eq)]
3455#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3456pub struct Msid {
3457 pub id: String,
3459 pub appdata: Option<String>,
3461}
3462
3463impl Msid {
3464 pub fn new(id: impl ToString, appdata: Option<impl ToString>) -> Self {
3465 Msid {
3466 id: id.to_string(),
3467 appdata: appdata.map(|appdata| appdata.to_string()),
3468 }
3469 }
3470}
3471
3472impl FromStr for Msid {
3473 type Err = AttributeError;
3474
3475 fn from_str(s: &str) -> Result<Self, Self::Err> {
3476 let attr = <Self as TypedAttribute>::NAME.to_string();
3477 let mut tokens = s.split_ascii_whitespace();
3478
3479 let id = tokens.next().ok_or_else(|| AttributeError::ParamNotFound {
3480 param: "MSID id".to_string(),
3481 attr: attr.clone(),
3482 })?;
3483 let appdata = tokens.next();
3484
3485 let trailing: Vec<_> = tokens.collect();
3486 if !trailing.is_empty() {
3487 return Err(AttributeError::UnexpectedTrailingItem {
3488 val: trailing.join(" "),
3489 attr,
3490 });
3491 }
3492
3493 if id.len() > 64 || !is_token(id.as_bytes()) {
3495 return Err(AttributeError::InvalidParamValue {
3496 param: "MSID id".to_string(),
3497 val: id.to_string(),
3498 attr,
3499 });
3500 }
3501
3502 if let Some(appdata) = appdata {
3503 if appdata.len() > 64 || !is_token(appdata.as_bytes()) {
3504 return Err(AttributeError::InvalidParamValue {
3505 param: "MSID appdata".to_string(),
3506 val: appdata.to_string(),
3507 attr,
3508 });
3509 }
3510 }
3511
3512 Ok(Msid {
3513 id: id.to_string(),
3514 appdata: appdata.map(ToString::to_string),
3515 })
3516 }
3517}
3518
3519impl Display for Msid {
3520 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3521 f.write_str(&self.id)?;
3522 if let Some(appdata) = &self.appdata {
3523 f.write_char(' ')?;
3524 f.write_str(appdata)?;
3525 }
3526 Ok(())
3527 }
3528}
3529
3530impl TypedAttribute for Msid {
3531 const NAME: &'static str = "msid";
3532}
3533
3534#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3543#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3544pub struct SctpPort(pub u16);
3545
3546fn parse_1_5_digits(s: &str) -> Option<u16> {
3548 if s.is_empty()
3549 || s.len() > 5
3550 || !s.chars().all(|c| c.is_ascii_digit())
3551 || (s.len() > 1 && s.starts_with('0'))
3552 {
3553 return None;
3554 }
3555
3556 s.parse().ok()
3557}
3558
3559impl FromStr for SctpPort {
3560 type Err = AttributeError;
3561
3562 fn from_str(s: &str) -> Result<Self, Self::Err> {
3563 let invalid = || AttributeError::InvalidParamValue {
3564 param: "SCTP port".to_string(),
3565 val: s.to_string(),
3566 attr: <Self as TypedAttribute>::NAME.to_string(),
3567 };
3568
3569 let port = parse_1_5_digits(s).ok_or_else(invalid)?;
3571 Ok(SctpPort(port))
3572 }
3573}
3574
3575impl Display for SctpPort {
3576 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3577 write!(f, "{}", self.0)
3578 }
3579}
3580
3581impl TypedAttribute for SctpPort {
3582 const NAME: &'static str = "sctp-port";
3583}
3584
3585#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3594#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3595pub struct MaxMessageSize(pub u64);
3596
3597impl FromStr for MaxMessageSize {
3598 type Err = AttributeError;
3599
3600 fn from_str(s: &str) -> Result<Self, Self::Err> {
3601 let invalid = || AttributeError::InvalidParamValue {
3602 param: "Max message size".to_string(),
3603 val: s.to_string(),
3604 attr: <Self as TypedAttribute>::NAME.to_string(),
3605 };
3606
3607 let size = parse_decimal_no_leading_zeroes(s).ok_or_else(invalid)?;
3609 Ok(MaxMessageSize(size))
3610 }
3611}
3612
3613impl Display for MaxMessageSize {
3614 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3615 write!(f, "{}", self.0)
3616 }
3617}
3618
3619impl TypedAttribute for MaxMessageSize {
3620 const NAME: &'static str = "max-message-size";
3621}
3622
3623#[derive(Debug, Clone, PartialEq, Eq)]
3633#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3634pub struct SctpMap {
3635 pub number: u16,
3637 pub app: String,
3640 pub streams: Option<u16>,
3642}
3643
3644impl SctpMap {
3645 pub fn new(number: u16, app: impl ToString, streams: Option<u16>) -> Self {
3646 SctpMap {
3647 number,
3648 app: app.to_string(),
3649 streams,
3650 }
3651 }
3652}
3653
3654impl FromStr for SctpMap {
3655 type Err = AttributeError;
3656
3657 fn from_str(s: &str) -> Result<Self, Self::Err> {
3658 let attr = <Self as TypedAttribute>::NAME.to_string();
3659 let mut tokens = s.split_ascii_whitespace();
3660
3661 let number = tokens.next().ok_or_else(|| AttributeError::ParamNotFound {
3662 param: "SCTP port number".to_string(),
3663 attr: attr.clone(),
3664 })?;
3665 let number = parse_1_5_digits(number).ok_or_else(|| AttributeError::InvalidParamValue {
3666 param: "SCTP port number".to_string(),
3667 val: number.to_string(),
3668 attr: attr.clone(),
3669 })?;
3670
3671 let app = tokens.next().ok_or_else(|| AttributeError::ParamNotFound {
3672 param: "Application".to_string(),
3673 attr: attr.clone(),
3674 })?;
3675 if !is_token(app.as_bytes()) {
3676 return Err(AttributeError::InvalidParamValue {
3677 param: "Application".to_string(),
3678 val: app.to_string(),
3679 attr,
3680 });
3681 }
3682
3683 let streams = match tokens.next() {
3684 Some(streams) => Some(parse_1_5_digits(streams).ok_or_else(|| {
3685 AttributeError::InvalidParamValue {
3686 param: "Stream count".to_string(),
3687 val: streams.to_string(),
3688 attr: attr.clone(),
3689 }
3690 })?),
3691 None => None,
3692 };
3693
3694 let trailing: Vec<_> = tokens.collect();
3695 if !trailing.is_empty() {
3696 return Err(AttributeError::UnexpectedTrailingItem {
3697 val: trailing.join(" "),
3698 attr,
3699 });
3700 }
3701
3702 Ok(SctpMap {
3703 number,
3704 app: app.to_string(),
3705 streams,
3706 })
3707 }
3708}
3709
3710impl Display for SctpMap {
3711 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3712 write!(f, "{} {}", self.number, self.app)?;
3713 if let Some(streams) = self.streams {
3714 write!(f, " {}", streams)?;
3715 }
3716 Ok(())
3717 }
3718}
3719
3720impl TypedAttribute for SctpMap {
3721 const NAME: &'static str = "sctpmap";
3722}
3723
3724#[cfg(test)]
3725mod tests {
3726 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3727
3728 use super::*;
3729 use crate::*;
3730
3731 #[test]
3732 fn direction_parse() {
3733 assert_eq!("sendonly".parse::<Direction>(), Ok(Direction::SendOnly));
3734 assert_eq!("recvonly".parse::<Direction>(), Ok(Direction::RecvOnly));
3735 assert_eq!("sendrecv".parse::<Direction>(), Ok(Direction::SendRecv));
3736 assert_eq!("inactive".parse::<Direction>(), Ok(Direction::Inactive));
3737 assert!("invalid".parse::<Direction>().is_err());
3738 }
3739
3740 #[test]
3741 fn direction_display() {
3742 assert_eq!(Direction::SendOnly.to_string(), "sendonly");
3743 assert_eq!(Direction::RecvOnly.to_string(), "recvonly");
3744 assert_eq!(Direction::SendRecv.to_string(), "sendrecv");
3745 assert_eq!(Direction::Inactive.to_string(), "inactive");
3746 }
3747
3748 #[test]
3749 fn parse_rtcp_fb() {
3750 let sdp = "v=0\r
3751o=alice 3203093520 3203093520 IN IP4 host.example.com\r
3752s=Multicast video with feedback\r
3753t=3203130148 3203137348\r
3754m=audio 49170 RTP/AVP 0\r
3755c=IN IP4 224.2.1.183\r
3756a=rtpmap:0 PCMU/8000\r
3757m=video 51372 RTP/AVPF 98 99\r
3758c=IN IP4 224.2.1.184\r
3759a=rtpmap:98 H263-1998/90000\r
3760a=rtpmap:99 H261/90000\r
3761a=rtcp-fb:* nack\r
3762a=rtcp-fb:98 nack rpsi\r
3763a=rtcp-fb:* trr-int 1000\r
3764a=rtcp-fb:98 ccm vbcm 1 2\r
3765a=rtcp-fb:* ccm tmmbr smaxpr=120\r
3766";
3767
3768 let parsed = Session::parse(sdp.as_bytes()).unwrap();
3769 let mut written = vec![];
3770 parsed.write(&mut written).unwrap();
3771
3772 let v = fallible_iterator::convert(parsed.medias[1].attributes_typed::<RtcpFb>())
3773 .collect::<Vec<_>>()
3774 .expect("Valid vector of attributes");
3775 assert_eq!(v[0].pt, RtcpFbPt::Wildcard);
3776 assert_eq!(v[1].val, RtcpFbVal::Nack(Some(RtcpFbNack::Rpsi)));
3777 assert_eq!(v[2].val, RtcpFbVal::TrrInt(1000));
3778 assert_eq!(v[3].val, RtcpFbVal::Ccm(RtcpFbCcm::Vbcm(vec![1, 2])));
3779 assert_eq!(
3780 v[4].val,
3781 RtcpFbVal::Ccm(RtcpFbCcm::Tmmbr(Some("smaxpr=120".to_string())))
3782 );
3783 }
3784
3785 #[test]
3786 fn parse_group_attribute() {
3787 let sdp = "v=0\r
3788o=Laura 289083124 289083124 IN IP4 two.example.com\r
3789c=IN IP4 233.252.0.1/127\r
3790t=0 0\r
3791a=group:LS 1 2\r
3792m=audio 30000 RTP/AVP 0\r
3793a=mid:1\r
3794m=video 30002 RTP/AVP 31\r
3795a=mid:2\r
3796m=audio 30004 RTP/AVP 0\r
3797i=This media stream contains the Spanish translation\r
3798a=mid:3\r
3799";
3800 let parsed = Session::parse(sdp.as_bytes()).unwrap();
3801
3802 let g = parsed.attributes_typed::<Group>().collect::<Vec<_>>();
3803 assert_eq!(g.len(), 1);
3804 assert_eq!(g[0].as_ref().unwrap().semantics, GroupSemantics::LS);
3805 assert_eq!(
3806 g[0].as_ref().unwrap().mid_tags,
3807 vec!["1".to_string(), "2".to_string()]
3808 );
3809 }
3810
3811 #[test]
3812 fn parse_setup_attribute() {
3813 let sdp = "v=0\r
3814m=image 54111 TCP t38\r
3815c=IN IP4 192.0.2.2\r
3816a=setup:actpass\r
3817a=connection:new\r
3818";
3819 let media = Session::parse(sdp.as_bytes()).unwrap().medias;
3820
3821 let s = media[0].attributes_typed::<Setup>().collect::<Vec<_>>();
3822
3823 assert_eq!(s.len(), 1);
3824 assert_eq!(s[0].as_ref().unwrap().to_owned(), Setup::ActPass);
3825 }
3826
3827 #[test]
3828 fn parse_ssrc_attributes() {
3829 let sdp = "v=0\r
3830o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r
3831m=video 49174 RTP/AVPF 96 98\r
3832a=rtpmap:98 rtx/90000\r
3833a=fmtp:98 apt=96;rtx-time=3000\r
3834a=ssrc-group:FID 11111 22222\r
3835a=ssrc:11111 cname:user3@example.com\r
3836a=ssrc:22222 fmtp:0 0-15\r
3837a=ssrc-group:FID 33333 44444\r
3838a=ssrc:33333 cname:user3@example.com\r
3839a=ssrc:44444 cname:user3@example.com\r
3840a=ssrc:1698359993 rtcp:5003 IN IP4 127.0.0.1
3841";
3842
3843 let parsed = Session::parse(sdp.as_bytes()).unwrap();
3844 let m = &parsed.medias[0];
3845
3846 let ssrcs = m
3847 .attributes_typed::<Ssrc>()
3848 .filter(|s| {
3849 let Ok(ssrc) = s else { return false };
3850 ssrc.attribute == SsrcAttribute::Fmtp || ssrc.attribute == SsrcAttribute::Rtcp
3851 })
3852 .collect::<Vec<_>>();
3853
3854 let ssrc_id = ssrcs[0].as_ref().unwrap().ssrc_id;
3855
3856 let ssrc_groups = m
3857 .attributes_typed::<SsrcGroup>()
3858 .filter(|s| {
3859 let Ok(ssrc_group) = s else { return false };
3860
3861 ssrc_group.ssrc_ids[1] == ssrc_id
3862 })
3863 .collect::<Vec<_>>();
3864
3865 assert_eq!(
3866 ssrc_groups[0].as_ref().unwrap().semantics,
3867 GroupSemantics::FID
3868 );
3869
3870 assert_eq!(ssrcs[1].as_ref().unwrap().attribute, SsrcAttribute::Rtcp);
3871 }
3872
3873 #[test]
3874 fn parse_crypto_attributes() {
3875 let sdp = "v=0\r
3876o=sam 2890844526 2890842807 IN IP4 10.47.16.5\r
3877s=SRTP Discussion\r
3878i=A discussion of Secure RTP\r
3879u=http://www.example.com/seminars/srtp.pdf\r
3880e=marge@example.com (Marge Simpson)\r
3881c=IN IP4 168.2.17.12\r
3882t=2873397496 2873404696\r
3883m=audio 49170 RTP/SAVP 0\r
3884a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4 FEC_ORDER=SRTP_FEC\r
3885a=crypto:2 F8_128_HMAC_SHA1_80 inline:MTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5QUJjZGVm|2^20|1:4;inline:QUJjZGVmMTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5|2^20|2:4 FEC_ORDER=FEC_SRTP\r
3886m=video 51372 RTP/SAVP 31\r
3887a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2|1066:4\r
3888";
3889
3890 let parsed = Session::parse(sdp.as_bytes()).unwrap();
3891 let a = &parsed.medias[0];
3892
3893 let audio_cryptos = a.attributes_typed::<Crypto>().collect::<Vec<_>>();
3894
3895 assert_eq!(
3896 audio_cryptos[0].as_ref().unwrap().crypto_suite,
3897 CryptoSuite::AesCm128HmacSha1_80
3898 );
3899 assert_eq!(audio_cryptos[1].as_ref().unwrap().key_params.len(), 2);
3900
3901 assert_eq!(
3902 audio_cryptos[1].as_ref().unwrap().key_params[1].mki_and_length,
3903 Some((2, 4))
3904 );
3905
3906 assert_eq!(
3907 audio_cryptos[1].as_ref().unwrap().session_params[0],
3908 SrtpSessionParam::FecOrder(FecOrder::FecSrtp)
3909 );
3910
3911 let v = &parsed.medias[1];
3912
3913 let video_cryptos = v
3914 .attributes_typed::<Crypto>()
3915 .filter(|c| {
3916 let Ok(crypto) = c else { return false };
3917
3918 crypto.tag == 1
3919 })
3920 .collect::<Vec<_>>();
3921
3922 let test_crypto = Crypto {
3923 tag: 1,
3924 crypto_suite: CryptoSuite::AesCm128HmacSha1_80,
3925 key_params: vec![SrtpKeyParam {
3926 key_and_salt: "YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2".to_string(),
3927 lifetime: None,
3928 mki_and_length: Some((1066, 4)),
3929 }],
3930 session_params: Vec::new(),
3931 };
3932
3933 assert_eq!(&test_crypto, video_cryptos[0].as_ref().unwrap());
3934 }
3935
3936 #[test]
3937 fn write_crypto_attribute() {
3938 let crypto = Crypto {
3939 tag: 1,
3940 crypto_suite: CryptoSuite::AesCm128HmacSha1_80,
3941 key_params: vec![
3942 SrtpKeyParam {
3943 key_and_salt: "WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz".to_string(),
3944 lifetime: Some(1048576),
3945 mki_and_length: Some((1, 4)),
3946 },
3947 SrtpKeyParam {
3948 key_and_salt: "WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz".to_string(),
3949 lifetime: Some(1048576),
3950 mki_and_length: Some((1, 4)),
3951 },
3952 ],
3953 session_params: vec![SrtpSessionParam::FecOrder(FecOrder::SrtpFec)],
3954 };
3955
3956 assert_eq!(
3957 crypto.to_string(),
3958 "1 AES_CM_128_HMAC_SHA1_80 inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4;inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4 FEC_ORDER=SRTP_FEC"
3959 );
3960 }
3961
3962 #[test]
3963 fn parse_srtp_key_param_lifetime() {
3964 let param = SrtpKeyParam::from_str("inline:YWJj|2^48").unwrap();
3966 assert_eq!(param.lifetime, Some(1 << 48));
3967 assert_eq!(param.to_string(), "inline:YWJj|2^48");
3968
3969 let param = SrtpKeyParam::from_str("inline:YWJj|1000000000000").unwrap();
3970 assert_eq!(param.lifetime, Some(1_000_000_000_000));
3971 assert_eq!(param.to_string(), "inline:YWJj|1000000000000");
3972
3973 for invalid in [
3974 "inline:YWJj|2^49",
3976 "inline:YWJj|2^100",
3977 "inline:YWJj|281474976710657",
3978 "inline:YWJj|0",
3980 "inline:YWJj|007",
3982 "inline:YWJj|0123",
3983 "inline:YWJj|2^007",
3984 "inline:YWJj|+1048576",
3985 "inline:YWJj|2^+20",
3986 ] {
3987 assert!(
3988 SrtpKeyParam::from_str(invalid).is_err(),
3989 "{} should be invalid",
3990 invalid
3991 );
3992 }
3993 }
3994
3995 #[test]
3996 fn parse_candidate_attributes() {
3997 use std::net::{Ipv4Addr, Ipv6Addr};
3998
3999 let sdp = "v=0\r
4000o=- 2890844526 2890842807 IN IP4 192.168.1.1\r
4001s=-\r
4002c=IN IP4 192.168.1.1\r
4003t=0 0\r
4004m=audio 49152 RTP/AVP 0\r
4005a=candidate:1 1 UDP 2130706432 192.168.1.1 49152 typ host raddr 10.0.1.1 rport 49153 generation 0\r
4006a=candidate:2 1 UDP 1692467200 10.0.1.1 49152 typ srflx raddr 192.168.1.1 rport 49153\r
4007a=candidate:3 2 UDP 1692467184 192.168.1.1 49153 typ host\r
4008a=candidate:4 1 UDP 100 2001:db8::1 49152 typ host\r
4009a=candidate:5 1 UDP 50 192.168.1.1 49154 typ prflx\r
4010a=candidate:6 1 UDP 25 192.168.1.1 49155 typ relay raddr 10.0.0.1 rport 49156\r
4011a=candidate:7 1 UDP 10 192.168.1.1 49157 typ unknown_type\r
4012";
4013
4014 let session = Session::parse(sdp.as_bytes()).unwrap();
4015 let candidates: Vec<Candidate> =
4016 fallible_iterator::convert(session.medias[0].attributes_typed::<Candidate>())
4017 .collect::<Vec<_>>()
4018 .expect("Valid vector of candidates");
4019
4020 assert_eq!(candidates.len(), 7);
4021
4022 assert_eq!(candidates[0].foundation, "1");
4023 assert_eq!(candidates[0].component_id, 1);
4024 assert_eq!(
4025 candidates[0].address,
4026 CandidateAddress::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)))
4027 );
4028 assert_eq!(candidates[0].port, 49152);
4029 assert_eq!(candidates[0].typ, CandidateType::Host);
4030 assert_eq!(
4031 candidates[0].rel_addr,
4032 Some(IpAddr::V4(Ipv4Addr::new(10, 0, 1, 1)))
4033 );
4034 assert_eq!(candidates[0].rel_port, Some(49153));
4035 assert_eq!(
4036 candidates[0].extensions,
4037 vec![("generation".to_string(), "0".to_string())]
4038 );
4039
4040 assert_eq!(candidates[1].foundation, "2");
4041 assert_eq!(candidates[1].typ, CandidateType::Srflx);
4042 assert_eq!(
4043 candidates[1].rel_addr,
4044 Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)))
4045 );
4046 assert_eq!(candidates[1].rel_port, Some(49153));
4047
4048 assert_eq!(candidates[2].foundation, "3");
4049 assert_eq!(candidates[2].component_id, 2);
4050 assert_eq!(candidates[2].typ, CandidateType::Host);
4051
4052 assert_eq!(candidates[3].foundation, "4");
4053 assert_eq!(
4054 candidates[3].address,
4055 CandidateAddress::IpAddr(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)))
4056 );
4057 assert_eq!(candidates[3].typ, CandidateType::Host);
4058
4059 assert_eq!(candidates[4].foundation, "5");
4060 assert_eq!(candidates[4].typ, CandidateType::Prflx);
4061
4062 assert_eq!(candidates[5].foundation, "6");
4063 assert_eq!(candidates[5].typ, CandidateType::Relay);
4064
4065 assert_eq!(candidates[6].foundation, "7");
4066 assert_eq!(
4067 candidates[6].typ,
4068 CandidateType::Other("unknown_type".to_string())
4069 );
4070 }
4071
4072 #[test]
4073 fn write_candidate() {
4074 use std::net::Ipv4Addr;
4075
4076 let candidate = Candidate {
4077 foundation: "abcd/1234".into(),
4078 component_id: 1,
4079 transport: "UDP".into(),
4080 priority: 2130706432,
4081 address: CandidateAddress::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1))),
4082 port: 49152,
4083 typ: CandidateType::Srflx,
4084 rel_addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
4085 rel_port: Some(49153),
4086 extensions: vec![("tcptype".to_string(), "active".to_string())],
4087 };
4088
4089 assert_eq!(
4090 candidate.to_string(),
4091 "abcd/1234 1 UDP 2130706432 192.168.0.1 49152 typ srflx raddr 10.0.0.1 rport 49153 tcptype active"
4092 );
4093 }
4094
4095 #[test]
4096 fn test_attribute_errors() {
4097 assert_eq!(
4099 "99".parse::<RtpMap>().unwrap_err(),
4100 AttributeError::UnsupportedFormat {
4101 val: "99".to_string(),
4102 attr: "rtpmap".to_string()
4103 }
4104 );
4105 assert_eq!(
4106 "abc 90000".parse::<RtpMap>().unwrap_err(),
4107 AttributeError::InvalidParamValue {
4108 param: "Payload type".to_string(),
4109 val: "abc".to_string(),
4110 attr: "rtpmap".to_string()
4111 }
4112 );
4113 assert_eq!(
4114 "200 enc/90000".parse::<RtpMap>().unwrap_err(),
4115 AttributeError::InvalidParamValue {
4116 param: "Payload type".to_string(),
4117 val: "200(expected 0-127)".to_string(),
4118 attr: "rtpmap".to_string()
4119 }
4120 );
4121 assert_eq!(
4122 "99 ".parse::<RtpMap>().unwrap_err(),
4123 AttributeError::ParamNotFound {
4124 param: "Clock rate".to_string(),
4125 attr: "rtpmap".to_string()
4126 }
4127 );
4128 assert_eq!(
4129 "99 /".parse::<RtpMap>().unwrap_err(),
4130 AttributeError::InvalidParamValue {
4131 param: "Clock rate".to_string(),
4132 val: "".to_string(),
4133 attr: "rtpmap".to_string()
4134 }
4135 );
4136
4137 assert_eq!(
4138 "invalid".parse::<Fmtp>().unwrap_err(),
4139 AttributeError::UnsupportedFormat {
4140 val: "invalid".to_string(),
4141 attr: "fmtp".to_string()
4142 }
4143 );
4144 assert_eq!(
4145 "abc profile=1".parse::<Fmtp>().unwrap_err(),
4146 AttributeError::InvalidParamValue {
4147 param: "fmtp".to_string(),
4148 val: "abc".to_string(),
4149 attr: "fmtp".to_string()
4150 }
4151 );
4152
4153 assert_eq!(
4155 "".parse::<Rtcp>().unwrap_err(),
4156 AttributeError::ParamNotFound {
4157 param: "Port".to_string(),
4158 attr: "rtcp".to_string()
4159 }
4160 );
4161 assert_eq!(
4162 "abc IN IP4 127.0.0.1".parse::<Rtcp>().unwrap_err(),
4163 AttributeError::InvalidParamValue {
4164 param: "Port".to_string(),
4165 val: "abc".to_string(),
4166 attr: "rtcp".to_string()
4167 }
4168 );
4169
4170 assert_eq!(
4172 "".parse::<Fingerprint>().unwrap_err(),
4173 AttributeError::ParamNotFound {
4174 param: "Hash value".to_string(),
4175 attr: "fingerprint".to_string()
4176 }
4177 );
4178 assert_eq!(
4179 "SHA-1".parse::<Fingerprint>().unwrap_err(),
4180 AttributeError::ParamNotFound {
4181 param: "Hash value".to_string(),
4182 attr: "fingerprint".to_string()
4183 }
4184 );
4185
4186 assert_eq!(
4188 "".parse::<Candidate>().unwrap_err(),
4189 AttributeError::ParamNotFound {
4190 param: "Component id".to_string(),
4191 attr: "candidate".to_string()
4192 }
4193 );
4194 assert_eq!(
4195 "1 1 UDP 100".parse::<Candidate>().unwrap_err(),
4196 AttributeError::ParamNotFound {
4197 param: "Address".to_string(),
4198 attr: "candidate".to_string()
4199 }
4200 );
4201
4202 assert_eq!(
4204 "".parse::<ExtMap>().unwrap_err(),
4205 AttributeError::InvalidParamValue {
4206 param: "Id".to_string(),
4207 val: "".to_string(),
4208 attr: "extmap".to_string()
4209 }
4210 );
4211 assert_eq!(
4212 "999999 http://example.com".parse::<ExtMap>().unwrap_err(),
4213 AttributeError::InvalidParamValue {
4214 param: "Id".to_string(),
4215 val: "999999".to_string(),
4216 attr: "extmap".to_string()
4217 }
4218 );
4219
4220 assert_eq!(
4222 "".parse::<Group>().unwrap_err(),
4223 AttributeError::ParamNotFound {
4224 param: "Semantics".to_string(),
4225 attr: "group".to_string()
4226 }
4227 );
4228 assert_eq!(
4230 "LS".parse::<Group>().unwrap(),
4231 Group {
4232 semantics: GroupSemantics::LS,
4233 mid_tags: vec![],
4234 }
4235 );
4236 let group = "bundle\t0 1 ".parse::<Group>().unwrap();
4239 assert_eq!(group.semantics, GroupSemantics::Bundle);
4240 assert_eq!(group.mid_tags, vec!["0".to_string(), "1".to_string()]);
4241 assert_eq!(group.to_string(), "BUNDLE 0 1");
4242
4243 let ssrc_group = "FEC\t123 456".parse::<SsrcGroup>().unwrap();
4245 assert_eq!(ssrc_group.semantics, GroupSemantics::FEC);
4246 assert_eq!(ssrc_group.ssrc_ids, vec![123, 456]);
4247
4248 assert_eq!(
4250 "".parse::<Ssrc>().unwrap_err(),
4251 AttributeError::ParamNotFound {
4252 param: "Ssrc id".to_string(),
4253 attr: "ssrc".to_string()
4254 }
4255 );
4256 assert_eq!(
4257 "abc".parse::<Ssrc>().unwrap_err(),
4258 AttributeError::ParamNotFound {
4259 param: "Ssrc id".to_string(),
4260 attr: "ssrc".to_string()
4261 }
4262 );
4263
4264 let setup_err = "foo".parse::<Setup>().err().unwrap();
4266 assert!(matches!(setup_err, AttributeError::Other { .. }));
4267 assert_eq!(format!("{}", setup_err), "setup: Invalid Setup value foo");
4268
4269 assert_eq!(
4271 "".parse::<Crypto>().unwrap_err(),
4272 AttributeError::InvalidParamValue {
4273 param: "Tag".to_string(),
4274 val: "".to_string(),
4275 attr: "crypto".to_string()
4276 }
4277 );
4278 assert_eq!(
4279 "abc AES_CM_128_HMAC_SHA1_32 inline:key"
4280 .parse::<Crypto>()
4281 .unwrap_err(),
4282 AttributeError::InvalidParamValue {
4283 param: "Tag".to_string(),
4284 val: "abc".to_string(),
4285 attr: "crypto".to_string()
4286 }
4287 );
4288
4289 assert_eq!(
4291 "".parse::<RtcpFb>().unwrap_err(),
4292 AttributeError::InvalidParamValue {
4293 param: "Payload format".to_string(),
4294 val: "".to_string(),
4295 attr: "rtcp-fb".to_string()
4296 }
4297 );
4298 assert_eq!(
4299 "*".parse::<RtcpFb>().unwrap_err(),
4300 AttributeError::ParamNotFound {
4301 param: "Rtcp feedback value".to_string(),
4302 attr: "rtcp-fb".to_string()
4303 }
4304 );
4305 assert_eq!(
4306 "1 ack ccfb".parse::<RtcpFb>().unwrap_err(),
4307 AttributeError::InvalidParamValue {
4308 param: "Payload type of Congestion control feedback (ccfb)".to_string(),
4309 val: "1(expected wildcard (*))".to_string(),
4310 attr: "rtcp-fb".to_string()
4311 }
4312 );
4313
4314 let media = Media {
4316 media: "video".into(),
4317 port: 1234,
4318 num_ports: None,
4319 proto: "RTP/SAVPF".into(),
4320 fmt: "".into(),
4321 media_title: None,
4322 connections: vec![],
4323 bandwidths: vec![],
4324 key: None,
4325 attributes: vec![Attribute {
4326 attribute: "rtpmap".into(),
4327 value: None,
4328 }],
4329 };
4330 assert_eq!(
4331 media
4332 .attributes_typed::<RtpMap>()
4333 .collect::<Vec<Result<RtpMap, AttributeError>>>()
4334 .remove(0)
4335 .unwrap_err(),
4336 AttributeError::UnsupportedFormat {
4337 val: String::new(),
4338 attr: "rtpmap".to_string()
4339 }
4340 );
4341 }
4342
4343 #[test]
4344 #[allow(deprecated)]
4345 fn parse_rtcp_address() {
4346 let sdp = "v=0\r
4347o=alice 3203093520 3203093520 IN IP4 host.example.com\r
4348s=parse rtcp attribute address test\r
4349a=rtcp:5000 IN IP4 127.0.0.1\r
4350a=rtcp:5000 IN IP4 127.0.0.0/24\r
4351a=rtcp:5000 IN IP6 ::1\r
4352a=rtcp:5000 IN NONIP non-IP\r
4353a=rtcp:53020\r
4354";
4355
4356 let parsed = Session::parse(sdp.as_bytes()).unwrap();
4357 let mut rtcp_attr_iter = parsed.attributes_typed::<Rtcp>();
4358
4359 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4360 assert_eq!(rtcp.addrtype, Some(AddrType::Ip4));
4361 assert_eq!(rtcp.connection_address.as_deref(), Some("127.0.0.1"));
4362 assert_eq!(
4363 rtcp.try_parse_connection_ip_address().unwrap().unwrap(),
4364 IpAddr::V4(Ipv4Addr::LOCALHOST),
4365 );
4366
4367 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4368 assert_eq!(rtcp.addrtype, Some(AddrType::Ip4));
4369 assert_eq!(rtcp.connection_address.as_deref(), Some("127.0.0.0/24"));
4370 assert_eq!(
4373 rtcp.try_parse_connection_ip_address().unwrap().unwrap(),
4374 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 0)),
4375 );
4376 let address = rtcp.try_address().unwrap().unwrap();
4377 assert_eq!(address.ttl, Some(24));
4378 assert_eq!(address.count, None);
4379 assert!(!rtcp.is_multicast());
4380
4381 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4382 assert_eq!(rtcp.addrtype, Some(AddrType::Ip6));
4383 assert_eq!(rtcp.connection_address.as_deref(), Some("::1"));
4384 assert_eq!(
4385 rtcp.try_parse_connection_ip_address().unwrap().unwrap(),
4386 IpAddr::V6(Ipv6Addr::LOCALHOST),
4387 );
4388
4389 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4390 assert_eq!(rtcp.addrtype, Some(AddrType::Other("NONIP".to_string())));
4391 assert_eq!(rtcp.connection_address.as_deref(), Some("non-IP"));
4392 assert_eq!(
4393 rtcp.try_parse_connection_ip_address().unwrap().unwrap_err(),
4394 "non-IP",
4395 );
4396
4397 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
4399 assert_eq!(rtcp.port, 53020);
4400 assert_eq!(rtcp.nettype, None);
4401 assert_eq!(rtcp.addrtype, None);
4402 assert_eq!(rtcp.connection_address, None);
4403 assert_eq!(rtcp.try_parse_connection_ip_address(), None);
4404 assert_eq!(rtcp.try_address(), None);
4405 assert!(!rtcp.is_multicast());
4406 assert_eq!(rtcp.to_string(), "53020");
4407 }
4408
4409 #[test]
4410 fn rtcp_multicast_address_helpers() {
4411 let rtcp = "5000 IN IP4 233.252.0.1/127/3".parse::<Rtcp>().unwrap();
4417 assert!(rtcp.is_multicast());
4418 assert_eq!(
4419 rtcp.try_address().unwrap().unwrap(),
4420 ConnectionAddress {
4421 ip: Ipv4Addr::new(233, 252, 0, 1).into(),
4422 ttl: Some(127),
4423 count: Some(3),
4424 }
4425 );
4426
4427 let mut rtcp = Rtcp::with_port(5000);
4430 rtcp.connection_address = Some("224.2.1.1/16".to_string());
4431 let address = rtcp.try_address().unwrap().unwrap();
4432 assert_eq!(address.ttl, Some(16));
4433 assert_eq!(address.count, None);
4434
4435 let mut rtcp = Rtcp::with_port(5000);
4436 rtcp.connection_address = Some("ff12::5/3".to_string());
4437 let address = rtcp.try_address().unwrap().unwrap();
4438 assert_eq!(address.ttl, None);
4439 assert_eq!(address.count, Some(3));
4440
4441 let rtcp = "5000 IN IP6 ff12::5/3".parse::<Rtcp>().unwrap();
4442 assert!(rtcp.is_ipv6());
4443 assert!(!rtcp.is_ipv4());
4444 assert_eq!(rtcp.try_is_multicast(), Some(Ok(true)));
4445
4446 let rtcp = Rtcp::with_port(5000);
4449 assert_eq!(rtcp.try_is_multicast(), None);
4450 assert!(!rtcp.is_multicast());
4451 assert!(!rtcp.is_ipv4());
4452 assert!(!rtcp.is_ipv6());
4453
4454 let rtcp = "5000 IN IP4 rtcp.example.com".parse::<Rtcp>().unwrap();
4456 assert!(!rtcp.is_multicast());
4457 assert!(!rtcp.is_ipv4());
4458 assert_eq!(
4459 rtcp.try_is_multicast(),
4460 Some(Err(ConnectionAddressError::InvalidAddress(
4461 "rtcp.example.com".to_string()
4462 )))
4463 );
4464 }
4465
4466 #[test]
4467 fn parse_rtcp_attribute_whitespace() {
4468 assert_eq!(
4472 "6000 IN IP4 127.0.0.1".parse::<Rtcp>().unwrap(),
4473 Rtcp::new(6000, NetType::In, AddrType::Ip4, "127.0.0.1")
4474 );
4475 assert_eq!(
4476 "6001 IN IP4 127.0.0.1 ".parse::<Rtcp>().unwrap(),
4477 Rtcp::new(6001, NetType::In, AddrType::Ip4, "127.0.0.1")
4478 );
4479 assert_eq!("53020 ".parse::<Rtcp>().unwrap(), Rtcp::with_port(53020));
4480
4481 assert!("6002 IN 127.0.0.1".parse::<Rtcp>().is_err());
4482 assert!("6003 IN IP4 127.0.0.1 extra".parse::<Rtcp>().is_err());
4483 }
4484
4485 #[test]
4486 fn parse_rtcp_nettype_and_addrtype_must_be_tokens() {
4487 assert_eq!(
4490 "6000 a<b IP4 127.0.0.1".parse::<Rtcp>().unwrap_err(),
4491 AttributeError::InvalidParamValue {
4492 param: "Network type".to_string(),
4493 val: "a<b".to_string(),
4494 attr: "rtcp".to_string(),
4495 }
4496 );
4497 assert_eq!(
4498 "6000 IN IP4/x 127.0.0.1".parse::<Rtcp>().unwrap_err(),
4499 AttributeError::InvalidParamValue {
4500 param: "Address type".to_string(),
4501 val: "IP4/x".to_string(),
4502 attr: "rtcp".to_string(),
4503 }
4504 );
4505 }
4506
4507 #[test]
4508 fn parse_rid_attribute() {
4509 let rid: Rid = "5 send pt=99,102;max-br=64000".parse().unwrap();
4511 assert_eq!(
4512 rid,
4513 Rid::with_params("5", RidDirection::Send, ["pt=99,102", "max-br=64000"])
4514 );
4515 assert_eq!(rid.to_string(), "5 send pt=99,102;max-br=64000");
4516
4517 let rid: Rid = "6 send pt=100,97,101,102".parse().unwrap();
4518 assert_eq!(
4519 rid,
4520 Rid::with_params("6", RidDirection::Send, ["pt=100,97,101,102"])
4521 );
4522
4523 let rid: Rid = "f recv max-br=19.5;apt=100".parse().unwrap();
4524 assert_eq!(
4525 rid,
4526 Rid::with_params("f", RidDirection::Recv, ["max-br=19.5", "apt=100"])
4527 );
4528 assert_eq!(rid.to_string(), "f recv max-br=19.5;apt=100");
4529
4530 let rid: Rid = "1 recv".parse().unwrap();
4532 assert_eq!(rid.direction, RidDirection::Recv);
4533 assert!(rid.params.is_empty());
4534 assert_eq!(rid.to_string(), "1 recv");
4535 assert_eq!(
4536 "2 send max-width".parse::<Rid>().unwrap(),
4537 Rid::with_params("2", RidDirection::Send, ["max-width"])
4538 );
4539
4540 assert_eq!(
4543 "2 send pt=99;".parse::<Rid>().unwrap(),
4544 Rid::with_params("2", RidDirection::Send, ["pt=99"])
4545 );
4546 assert!("2 SEND pt=99;".parse::<Rid>().is_err());
4547
4548 for invalid in [
4549 "",
4550 "1",
4551 " send",
4552 "1! send",
4555 "id:1 recv",
4556 "1 up",
4557 "1 send x y",
4558 "1 send pt=1;max br=2",
4559 ] {
4560 assert!(
4561 invalid.parse::<Rid>().is_err(),
4562 "expected error for {:?}",
4563 invalid
4564 );
4565 }
4566 }
4567
4568 #[test]
4569 fn parse_key_mgmt_attribute() {
4570 let key_mgmt: KeyMgmt = "mikey AAAAIklkAAAAdGV4dHRleHR0ZXh0".parse().unwrap();
4571 assert_eq!(
4572 key_mgmt,
4573 KeyMgmt::with_data(KeyMgmtMethod::Mikey, "AAAAIklkAAAAdGV4dHRleHR0ZXh0")
4574 );
4575 assert_eq!(key_mgmt.to_string(), "mikey AAAAIklkAAAAdGV4dHRleHR0ZXh0");
4576
4577 for method in ["rawurl", "MIKEY", "internal"] {
4580 let key_mgmt: KeyMgmt = format!("{} data", method).parse().unwrap();
4581 assert_eq!(key_mgmt.method, KeyMgmtMethod::Other(method.to_string()));
4582 assert_eq!(key_mgmt.data.as_deref(), Some("data"));
4583 assert_eq!(key_mgmt.to_string(), format!("{} data", method));
4584 }
4585
4586 let key_mgmt: KeyMgmt = "internal".parse().unwrap();
4588 assert_eq!(
4589 key_mgmt,
4590 KeyMgmt::new(KeyMgmtMethod::Other("internal".to_string()))
4591 );
4592 assert_eq!(key_mgmt.to_string(), "internal");
4593
4594 let key_mgmt: KeyMgmt = "mikey bar baz".parse().unwrap();
4596 assert_eq!(key_mgmt.data.as_deref(), Some("bar baz"));
4597 assert_eq!(key_mgmt.to_string(), "mikey bar baz");
4598
4599 assert!("".parse::<KeyMgmt>().is_err());
4600 assert!(" ".parse::<KeyMgmt>().is_err());
4601 assert!("mi!key data".parse::<KeyMgmt>().is_err());
4603 assert!("-mikey".parse::<KeyMgmt>().is_err());
4604 }
4605
4606 #[test]
4607 fn parse_remote_candidate_attribute() {
4608 let candidates: RemoteCandidates = "1 192.168.1.1 50000".parse().unwrap();
4609 assert_eq!(
4610 candidates,
4611 RemoteCandidates::new([RemoteCandidate::new(
4612 1,
4613 "192.168.1.1".parse::<IpAddr>().unwrap(),
4614 50000
4615 )])
4616 );
4617 assert_eq!(candidates.to_string(), "1 192.168.1.1 50000");
4618
4619 let candidates: RemoteCandidates = "1 192.0.2.3 45664 2 192.0.2.3 45665".parse().unwrap();
4621 assert_eq!(candidates.0.len(), 2);
4622 assert_eq!(candidates.0[1].component_id, 2);
4623 assert_eq!(
4624 candidates.to_string(),
4625 "1 192.0.2.3 45664 2 192.0.2.3 45665"
4626 );
4627
4628 let candidate: RemoteCandidate = "1 2001:db8::1 9".parse().unwrap();
4630 assert_eq!(candidate.address, "2001:db8::1".parse::<IpAddr>().unwrap());
4631 assert_eq!(candidate.to_string(), "1 2001:db8::1 9");
4632
4633 for invalid in [
4634 "",
4635 "1",
4636 "1 10.0.0.1",
4637 "x 10.0.0.1 50000",
4638 "1 x 50000",
4639 "1 example.com 50000",
4641 "1 10.0.0.1 70000",
4642 "1 10.0.0.1 50000 2",
4643 ] {
4644 assert!(
4645 invalid.parse::<RemoteCandidates>().is_err(),
4646 "expected error for {:?}",
4647 invalid
4648 );
4649 }
4650 }
4651
4652 #[test]
4653 fn parse_source_filter_attribute() {
4654 let filter: SourceFilter = "incl IN IP4 232.3.4.5 192.0.2.10".parse().unwrap();
4656 assert_eq!(filter.mode, SourceFilterMode::Incl);
4657 assert_eq!(filter.nettype, NetType::In);
4658 assert_eq!(filter.addrtype, SourceFilterAddrType::Ip4);
4659 assert_eq!(
4660 filter.destination,
4661 SourceFilterDestination::Address(SourceFilterAddress::IpAddr(
4662 "232.3.4.5".parse().unwrap()
4663 ))
4664 );
4665 assert_eq!(
4666 filter.sources,
4667 ["192.0.2.10".parse::<SourceFilterAddress>().unwrap()]
4668 );
4669 assert_eq!(filter.to_string(), "incl IN IP4 232.3.4.5 192.0.2.10");
4670
4671 let filter: SourceFilter = "excl IN IP4 192.0.2.11 192.0.2.10".parse().unwrap();
4673 assert_eq!(filter.mode, SourceFilterMode::Excl);
4674 assert_eq!(filter.to_string(), "excl IN IP4 192.0.2.11 192.0.2.10");
4675
4676 let filter: SourceFilter = "incl IN IP4 * 192.0.2.10".parse().unwrap();
4678 assert_eq!(filter.destination, SourceFilterDestination::Wildcard);
4679 assert_eq!(filter.to_string(), "incl IN IP4 * 192.0.2.10");
4680
4681 let filter: SourceFilter = "incl IN IP4 224.2.1.1 192.0.2.10 192.0.2.42"
4683 .parse()
4684 .unwrap();
4685 assert_eq!(filter.sources.len(), 2);
4686 assert_eq!(
4687 filter.to_string(),
4688 "incl IN IP4 224.2.1.1 192.0.2.10 192.0.2.42"
4689 );
4690
4691 let filter: SourceFilter = "incl IN IP6 ff0e::11a 2001:db8::1".parse().unwrap();
4693 assert_eq!(filter.addrtype, SourceFilterAddrType::Ip6);
4694 assert_eq!(
4695 filter.destination,
4696 SourceFilterDestination::Address(SourceFilterAddress::IpAddr(
4697 "ff0e::11a".parse().unwrap()
4698 ))
4699 );
4700 assert_eq!(filter.to_string(), "incl IN IP6 ff0e::11a 2001:db8::1");
4701
4702 let filter: SourceFilter = "incl IN * channel-1.example.com src-1.example.com"
4704 .parse()
4705 .unwrap();
4706 assert_eq!(filter.addrtype, SourceFilterAddrType::Any);
4707 assert_eq!(
4708 filter.destination,
4709 SourceFilterDestination::Address(SourceFilterAddress::FQDN(
4710 "channel-1.example.com".to_string()
4711 ))
4712 );
4713 assert_eq!(
4714 filter.sources,
4715 [SourceFilterAddress::FQDN("src-1.example.com".to_string())]
4716 );
4717 assert_eq!(
4718 filter.to_string(),
4719 "incl IN * channel-1.example.com src-1.example.com"
4720 );
4721
4722 let filter: SourceFilter = "EXCL IN IP6 ff0e::11a 2001:db8::1".parse().unwrap();
4725 assert_eq!(filter.mode, SourceFilterMode::Excl);
4726 assert_eq!(filter.addrtype, SourceFilterAddrType::Ip6);
4727 assert!("excl IN ip6 ff0e::11a 2001:db8::1"
4728 .parse::<SourceFilter>()
4729 .is_err());
4730
4731 for invalid in [
4732 "",
4733 "incl",
4734 "incl IN",
4735 "incl IN IP4",
4736 "incl IN IP4 232.3.4.5",
4737 "incl IN IP4 232.3.4.5/127 192.0.2.10",
4740 "incl IN * 232.3.4.5 192.0.2.10",
4742 "incl IN IP4 232.3.4.5 *",
4744 "unknown IN IP4 232.3.4.5 192.0.2.10",
4745 "incl IN(IP4 232.3.4.5 192.0.2.10",
4747 "incl IN IP8 232.3.4.5 192.0.2.10",
4748 ] {
4749 assert!(
4750 invalid.parse::<SourceFilter>().is_err(),
4751 "expected error for {:?}",
4752 invalid
4753 );
4754 }
4755 }
4756
4757 #[test]
4758 fn parse_mid_attribute() {
4759 let mid: Mid = "1".parse().unwrap();
4760 assert_eq!(mid, Mid("1".to_string()));
4761 assert_eq!(mid.to_string(), "1");
4762
4763 let attribute: Attribute = mid.clone().into();
4764 assert_eq!(attribute.attribute, "mid");
4765 assert_eq!(attribute.value.as_deref(), Some("1"));
4766
4767 let mid: Mid = "v2R1d1".parse().unwrap();
4769 assert_eq!(mid.to_string(), "v2R1d1");
4770
4771 for invalid in [
4772 "", " ", "1 2", "a:b",
4774 ] {
4775 assert!(
4776 invalid.parse::<Mid>().is_err(),
4777 "expected error for {:?}",
4778 invalid
4779 );
4780 }
4781 }
4782
4783 #[test]
4784 fn parse_rtcp_mux_attribute() {
4785 assert_eq!("".parse::<RtcpMux>().unwrap(), RtcpMux);
4786
4787 let attribute: Attribute = RtcpMux.into();
4788 assert_eq!(attribute.attribute, "rtcp-mux");
4789 assert_eq!(attribute.value, None);
4790
4791 assert!("1".parse::<RtcpMux>().is_err());
4793
4794 let sdp = "v=0\r
4795o=alice 3203093520 3203093520 IN IP4 host.example.com\r
4796s=Property attribute test\r
4797t=3203130148 3203137348\r
4798m=audio 49170 RTP/AVP 0\r
4799a=rtcp-mux\r
4800";
4801 let parsed = Session::parse(sdp.as_bytes()).unwrap();
4802 assert_eq!(
4803 parsed.medias[0].get_first_attribute_typed::<RtcpMux>(),
4804 Some(Ok(RtcpMux))
4805 );
4806 }
4807
4808 #[test]
4809 fn parse_rtcp_mux_only_attribute() {
4810 assert!(matches!("".parse::<RtcpMuxOnly>(), Ok(RtcpMuxOnly)));
4811
4812 let attribute: Attribute = RtcpMuxOnly.into();
4813 assert_eq!(attribute.attribute, "rtcp-mux-only");
4814 assert_eq!(attribute.value, None);
4815
4816 assert!("1".parse::<RtcpMuxOnly>().is_err());
4818 }
4819
4820 #[test]
4821 fn parse_rtcp_rsize_attribute() {
4822 assert!(matches!("".parse::<RtcpRsize>(), Ok(RtcpRsize)));
4823
4824 let attribute: Attribute = RtcpRsize.into();
4825 assert_eq!(attribute.attribute, "rtcp-rsize");
4826 assert_eq!(attribute.value, None);
4827
4828 assert!("1".parse::<RtcpRsize>().is_err());
4830 }
4831
4832 #[test]
4833 fn parse_ice_lite_attribute() {
4834 assert!(matches!("".parse::<IceLite>(), Ok(IceLite)));
4835
4836 let attribute: Attribute = IceLite.into();
4837 assert_eq!(attribute.attribute, "ice-lite");
4838 assert_eq!(attribute.value, None);
4839
4840 assert!("1".parse::<IceLite>().is_err());
4842 }
4843
4844 #[test]
4845 fn parse_ice_ufrag_attribute() {
4846 let ufrag: IceUfrag = "EpGh".parse().unwrap();
4847 assert_eq!(ufrag, IceUfrag("EpGh".to_string()));
4848 assert_eq!(ufrag.to_string(), "EpGh");
4849
4850 let attribute: Attribute = ufrag.into();
4851 assert_eq!(attribute.attribute, "ice-ufrag");
4852 assert_eq!(attribute.value.as_deref(), Some("EpGh"));
4853
4854 assert_eq!("a+Z/9".parse::<IceUfrag>().unwrap().to_string(), "a+Z/9");
4857
4858 assert!("a".repeat(256).parse::<IceUfrag>().is_ok());
4860
4861 let too_long = "a".repeat(257);
4862 for invalid in [
4863 "",
4864 "abc", too_long.as_str(), "Ep-Gh", "Ep Gh", "\u{30b0}pGh", ] {
4870 assert!(
4871 invalid.parse::<IceUfrag>().is_err(),
4872 "expected error for {:?}",
4873 invalid
4874 );
4875 }
4876 }
4877
4878 #[test]
4879 fn parse_ice_pwd_attribute() {
4880 let password = "aq1s2dq3w4er5ty6ui7p8o9i0u";
4881 let pwd: IcePwd = password.parse().unwrap();
4882 assert_eq!(pwd, IcePwd(password.to_string()));
4883 assert_eq!(pwd.to_string(), password);
4884
4885 let attribute: Attribute = pwd.into();
4886 assert_eq!(attribute.attribute, "ice-pwd");
4887 assert_eq!(attribute.value.as_deref(), Some(password));
4888
4889 let shortest = "a".repeat(22);
4891 assert_eq!(shortest.parse::<IcePwd>().unwrap().to_string(), shortest);
4892 assert!("a".repeat(256).parse::<IcePwd>().is_ok());
4893
4894 let too_long = "a".repeat(257);
4895 for invalid in [
4896 "",
4897 "a".repeat(21).as_str(), too_long.as_str(), "abcdefghijklmnopqr-stu", "\u{30b0}bcdefghijklmnopqrstuvw", ] {
4902 assert!(
4903 invalid.parse::<IcePwd>().is_err(),
4904 "expected error for {:?}",
4905 invalid
4906 );
4907 }
4908 }
4909
4910 #[test]
4911 fn parse_ice_options_attribute() {
4912 let options: IceOptions = "trickle".parse().unwrap();
4913 assert_eq!(options, IceOptions::new(["trickle"]));
4914 assert_eq!(options.to_string(), "trickle");
4915
4916 let options: IceOptions = "trickle renomination".parse().unwrap();
4918 assert_eq!(options, IceOptions::new(["trickle", "renomination"]));
4919 assert_eq!(options.to_string(), "trickle renomination");
4920
4921 let attribute: Attribute = options.into();
4922 assert_eq!(attribute.attribute, "ice-options");
4923 assert_eq!(attribute.value.as_deref(), Some("trickle renomination"));
4924
4925 for invalid in ["", " ", "tr-ickle"] {
4926 assert!(
4927 invalid.parse::<IceOptions>().is_err(),
4928 "expected error for {:?}",
4929 invalid
4930 );
4931 }
4932 }
4933
4934 #[test]
4935 fn parse_end_of_candidates_attribute() {
4936 assert!(matches!("".parse::<EndOfCandidates>(), Ok(EndOfCandidates)));
4937
4938 let attribute: Attribute = EndOfCandidates.into();
4939 assert_eq!(attribute.attribute, "end-of-candidates");
4940 assert_eq!(attribute.value, None);
4941
4942 assert!("1".parse::<EndOfCandidates>().is_err());
4944 }
4945
4946 #[test]
4947 fn parse_msid_attribute() {
4948 let msid: Msid = "examplefoo examplebar".parse().unwrap();
4950 assert_eq!(msid, Msid::new("examplefoo", Some("examplebar")));
4951 assert_eq!(msid.to_string(), "examplefoo examplebar");
4952
4953 let attribute: Attribute = msid.into();
4954 assert_eq!(attribute.attribute, "msid");
4955 assert_eq!(attribute.value.as_deref(), Some("examplefoo examplebar"));
4956
4957 let msid: Msid = "-".parse().unwrap();
4960 assert_eq!(msid, Msid::new("-", None::<&str>));
4961 assert_eq!(msid.to_string(), "-");
4962
4963 let too_long = "a".repeat(65);
4964 for invalid in [
4965 "",
4966 too_long.as_str(), "foo \u{fffd}", "foo bar baz", ] {
4970 assert!(
4971 invalid.parse::<Msid>().is_err(),
4972 "expected error for {:?}",
4973 invalid
4974 );
4975 }
4976 }
4977
4978 #[test]
4979 fn parse_sctp_port_attribute() {
4980 let port: SctpPort = "5000".parse().unwrap();
4982 assert_eq!(port, SctpPort(5000));
4983 assert_eq!(port.to_string(), "5000");
4984
4985 let attribute: Attribute = port.into();
4986 assert_eq!(attribute.attribute, "sctp-port");
4987 assert_eq!(attribute.value.as_deref(), Some("5000"));
4988
4989 assert_eq!("0".parse::<SctpPort>().unwrap(), SctpPort(0));
4991 assert_eq!("65535".parse::<SctpPort>().unwrap(), SctpPort(65535));
4992
4993 for invalid in [
4994 "", "65536", "99999", "05000", "500000", "abc", "50a",
4999 ] {
5000 assert!(
5001 invalid.parse::<SctpPort>().is_err(),
5002 "expected error for {:?}",
5003 invalid
5004 );
5005 }
5006 }
5007
5008 #[test]
5009 fn parse_max_message_size_attribute() {
5010 let size: MaxMessageSize = "100000".parse().unwrap();
5012 assert_eq!(size, MaxMessageSize(100000));
5013 assert_eq!(size.to_string(), "100000");
5014
5015 let attribute: Attribute = size.into();
5016 assert_eq!(attribute.attribute, "max-message-size");
5017 assert_eq!(attribute.value.as_deref(), Some("100000"));
5018
5019 assert_eq!("0".parse::<MaxMessageSize>().unwrap(), MaxMessageSize(0));
5021
5022 let too_large = "1".repeat(21);
5023 for invalid in [
5024 "",
5025 "01", "1_000",
5027 "abc",
5028 "10a",
5029 too_large.as_str(), ] {
5031 assert!(
5032 invalid.parse::<MaxMessageSize>().is_err(),
5033 "expected error for {:?}",
5034 invalid
5035 );
5036 }
5037 }
5038
5039 #[test]
5040 fn parse_sctpmap_attribute() {
5041 let map: SctpMap = "5000 webrtc-datachannel 256".parse().unwrap();
5043 assert_eq!(map, SctpMap::new(5000, "webrtc-datachannel", Some(256)));
5044 assert_eq!(map.to_string(), "5000 webrtc-datachannel 256");
5045
5046 let attribute: Attribute = map.into();
5047 assert_eq!(attribute.attribute, "sctpmap");
5048 assert_eq!(
5049 attribute.value.as_deref(),
5050 Some("5000 webrtc-datachannel 256")
5051 );
5052
5053 let map: SctpMap = "9 webrtc-datachannel".parse().unwrap();
5055 assert_eq!(map.streams, None);
5056 assert_eq!(map.to_string(), "9 webrtc-datachannel");
5057 assert_eq!("0 app 0".parse::<SctpMap>().unwrap().streams, Some(0));
5058
5059 for invalid in [
5060 "",
5061 "5000", "65536 app", "05000 app", "app app", "5000 app 70000", "5000 a:pp", "5000 app 256 extra", ] {
5069 assert!(
5070 invalid.parse::<SctpMap>().is_err(),
5071 "expected error for {:?}",
5072 invalid
5073 );
5074 }
5075 }
5076}