1use std::{net::IpAddr, str::FromStr};
90
91use bstr::*;
92use fallible_iterator::FallibleIterator;
93
94pub mod attributes;
95pub mod builders;
96pub mod clock_signalling;
97pub mod enums;
98mod parser;
99mod writer;
100
101pub use attributes::*;
102pub use clock_signalling::*;
103pub use enums::*;
104pub use parser::ParserError;
105
106#[derive(Debug, PartialEq, Eq, Clone)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
111pub struct Origin {
112 pub username: Option<String>,
114 pub sess_id: String,
118 pub sess_version: u64,
120 pub nettype: NetType,
122 pub addrtype: AddrType,
124 pub unicast_address: String,
126}
127
128impl Origin {
129 pub fn with_ip_addr(
131 sess_id: impl ToString,
132 sess_version: u64,
133 unicast_address: impl Into<IpAddr>,
134 ) -> Self {
135 let unicast_address = unicast_address.into();
136 Origin {
137 username: Default::default(),
138 sess_id: sess_id.to_string(),
139 sess_version,
140 nettype: NetType::In,
141 addrtype: unicast_address.into(),
142 unicast_address: unicast_address.to_string(),
143 }
144 }
145
146 pub fn new(
150 sess_id: impl ToString,
151 sess_version: u64,
152 nettype: NetType,
153 addrtype: AddrType,
154 unicast_address: impl ToString,
155 ) -> Self {
156 Origin {
157 username: Default::default(),
158 sess_id: sess_id.to_string(),
159 sess_version,
160 nettype,
161 addrtype,
162 unicast_address: unicast_address.to_string(),
163 }
164 }
165
166 pub fn set_unicast_ip_address(&mut self, unicast_address: impl Into<IpAddr>) {
168 let unicast_address = unicast_address.into();
169 self.addrtype = unicast_address.into();
170 self.unicast_address = unicast_address.to_string();
171 }
172 pub fn try_parse_unicast_ip_address(&self) -> Result<IpAddr, &str> {
176 self.unicast_address
177 .parse::<IpAddr>()
178 .map_err(|_| self.unicast_address.as_str())
179 }
180
181 pub fn set_username(&mut self, username: impl ToString) {
182 self.username = Some(username.to_string());
183 }
184
185 pub fn builder_with_ip_addr(
187 sess_id: impl ToString,
188 sess_version: u64,
189 unicast_address: impl Into<IpAddr>,
190 ) -> builders::Origin {
191 builders::Origin::with_ip_addr(sess_id, sess_version, unicast_address)
192 }
193
194 pub fn builder(
198 sess_id: impl ToString,
199 sess_version: u64,
200 nettype: NetType,
201 addrtype: AddrType,
202 unicast_address: impl ToString,
203 ) -> builders::Origin {
204 builders::Origin::new(sess_id, sess_version, nettype, addrtype, unicast_address)
205 }
206}
207
208#[derive(Debug, PartialEq, Eq, Clone)]
212#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
213pub struct Connection {
214 pub nettype: NetType,
216 pub addrtype: AddrType,
218 pub connection_address: String,
220}
221
222impl Connection {
223 pub fn from_ip_addr(connection_address: impl Into<IpAddr>) -> Self {
225 Self::from_ip_addr_ttl(connection_address, None)
226 }
227
228 pub fn from_ip_addr_ttl(connection_address: impl Into<IpAddr>, ttl: Option<u8>) -> Self {
235 let ip = connection_address.into();
236 let connection_address = match ttl {
237 Some(ttl) if ip.is_ipv4() => format!("{ip}/{ttl}"),
238 _ => ip.to_string(),
239 };
240 Connection {
241 nettype: NetType::In,
242 addrtype: ip.into(),
243 connection_address,
244 }
245 }
246
247 pub fn new(nettype: NetType, addrtype: AddrType, connection_address: impl ToString) -> Self {
251 Connection {
252 nettype,
253 addrtype,
254 connection_address: connection_address.to_string(),
255 }
256 }
257
258 pub fn set_connection_ip_address(&mut self, connection_address: impl Into<IpAddr>) {
260 let connection_address = connection_address.into();
261 self.addrtype = connection_address.into();
262 self.connection_address = connection_address.to_string();
263 }
264
265 #[deprecated(since = "0.3.1", note = "use Connection::try_address instead")]
274 pub fn try_parse_connection_ip_address(&self) -> Result<IpAddr, &str> {
275 parse_connection_ip_address(&self.connection_address)
276 }
277
278 pub fn try_address(&self) -> Result<ConnectionAddress, ConnectionAddressError> {
289 parse_connection_address(&self.connection_address, &self.addrtype)
290 }
291
292 pub fn is_multicast(&self) -> bool {
297 parse_connection_ip_address(&self.connection_address)
298 .map(|address| address.is_multicast())
299 .unwrap_or(false)
300 }
301
302 pub fn try_is_multicast(&self) -> Result<bool, ConnectionAddressError> {
306 parse_connection_ip_address(&self.connection_address)
307 .map(|address| address.is_multicast())
308 .map_err(|_| ConnectionAddressError::InvalidAddress(self.connection_address.clone()))
309 }
310
311 pub fn is_ipv4(&self) -> bool {
315 parse_connection_ip_address(&self.connection_address)
316 .map(|address| address.is_ipv4())
317 .unwrap_or(false)
318 }
319
320 pub fn is_ipv6(&self) -> bool {
324 parse_connection_ip_address(&self.connection_address)
325 .map(|address| address.is_ipv6())
326 .unwrap_or(false)
327 }
328}
329
330#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
335pub struct ConnectionAddress {
336 pub ip: IpAddr,
338 pub ttl: Option<u8>,
340 pub count: Option<u8>,
343}
344
345#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
347pub enum ConnectionAddressError {
348 #[error("invalid connection address '{0}'")]
351 InvalidAddress(String),
352 #[error("invalid time-to-live in connection address '{0}'")]
355 InvalidTtl(String),
356 #[error("invalid address count in connection address '{0}'")]
359 InvalidCount(String),
360}
361
362pub(crate) fn parse_connection_address(
365 address: &str,
366 addrtype: &AddrType,
367) -> Result<ConnectionAddress, ConnectionAddressError> {
368 let ip = parse_connection_ip_address(address)
369 .map_err(|_| ConnectionAddressError::InvalidAddress(address.to_string()))?;
370 let ttl = match parse_ttl(address, addrtype) {
371 Some(Ok(ttl)) => Some(ttl),
372 Some(Err(_)) => return Err(ConnectionAddressError::InvalidTtl(address.to_string())),
373 None => None,
374 };
375 let count = match parse_number_of_addresses(address, addrtype) {
376 Some(Ok(count)) => Some(count),
377 Some(Err(_)) => return Err(ConnectionAddressError::InvalidCount(address.to_string())),
378 None => None,
379 };
380
381 Ok(ConnectionAddress { ip, ttl, count })
382}
383
384pub(crate) fn parse_connection_ip_address(address: &str) -> Result<IpAddr, &str> {
390 let ip_part = match address.split_once('/') {
391 Some((ip_part, _suffix)) => ip_part,
392 None => address,
393 };
394 ip_part.parse::<IpAddr>().map_err(|_| address)
395}
396
397pub(crate) fn parse_ttl<'a>(address: &'a str, addrtype: &AddrType) -> Option<Result<u8, &'a str>> {
401 if addrtype != &AddrType::Ip4 {
402 return None;
403 }
404
405 let rest = address.split_once('/')?.1;
406 let ttl = match rest.split_once('/') {
407 Some((ttl, _count)) => ttl,
408 None => rest,
409 };
410 Some(ttl.parse::<u8>().map_err(|_| ttl))
411}
412
413pub(crate) fn parse_number_of_addresses<'a>(
417 address: &'a str,
418 addrtype: &AddrType,
419) -> Option<Result<u8, &'a str>> {
420 let rest = address.split_once('/')?.1;
421 let count = match addrtype {
422 AddrType::Ip4 => rest.split_once('/')?.1,
423 AddrType::Ip6 => rest,
424 AddrType::Other(_) => return None,
425 };
426 Some(count.parse::<u8>().map_err(|_| count))
427}
428
429impl<T: Into<IpAddr>> From<T> for Connection {
430 fn from(ip_addr: T) -> Self {
431 Connection::from_ip_addr(ip_addr)
432 }
433}
434
435#[derive(Debug, PartialEq, Eq, Clone)]
439#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
440pub struct Bandwidth {
441 pub bwtype: String,
443 pub bandwidth: u64,
445}
446
447impl Bandwidth {
448 pub fn new(bwtype: BandwidthType, bandwidth: u64) -> Self {
449 Bandwidth {
450 bwtype: bwtype.to_string(),
451 bandwidth,
452 }
453 }
454
455 pub fn try_parse_bwtype(&self) -> Result<BandwidthType, ParseEnumError> {
457 BandwidthType::from_str(self.bwtype.as_str())
458 }
459
460 pub fn set_bwtype(&mut self, bwtype: BandwidthType) {
462 self.bwtype = bwtype.to_string();
463 }
464}
465
466#[derive(Debug, PartialEq, Eq, Clone)]
470#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
471pub struct Time {
472 pub start_time: u64,
474 pub stop_time: u64,
476 pub repeats: Vec<Repeat>,
478}
479
480impl Time {
481 pub fn new(start_time: u64, stop_time: u64) -> Self {
482 Time {
483 start_time,
484 stop_time,
485 repeats: Default::default(),
486 }
487 }
488
489 pub fn add_repeat(&mut self, repeat: Repeat) {
490 self.repeats.push(repeat);
491 }
492
493 pub fn add_repeats(&mut self, repeats: impl IntoIterator<Item = Repeat>) {
494 self.repeats.extend(repeats)
495 }
496
497 pub fn builder(start_time: u64, stop_time: u64) -> builders::Time {
498 builders::Time::new(start_time, stop_time)
499 }
500}
501
502#[derive(Debug, PartialEq, Eq, Clone)]
506#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
507pub struct Repeat {
508 pub repeat_interval: u64,
510 pub active_duration: u64,
512 pub offsets: Vec<u64>,
514}
515
516impl Repeat {
517 pub fn new(repeat_interval: u64, active_duration: u64) -> Self {
518 Repeat {
519 repeat_interval,
520 active_duration,
521 offsets: Default::default(),
522 }
523 }
524
525 pub fn add_offset(&mut self, offset: u64) {
526 self.offsets.push(offset);
527 }
528
529 pub fn add_offsets(&mut self, offsets: impl IntoIterator<Item = u64>) {
530 self.offsets.extend(offsets)
531 }
532
533 pub fn builder(repeat_interval: u64, active_duration: u64) -> builders::Repeat {
534 builders::Repeat::new(repeat_interval, active_duration)
535 }
536}
537
538#[derive(Debug, PartialEq, Eq, Clone)]
542#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
543pub struct TimeZone {
544 pub adjustment_time: u64,
546 pub offset: i64,
548}
549
550impl TimeZone {
551 pub fn new(adjustment_time: u64, offset: i64) -> Self {
552 TimeZone {
553 adjustment_time,
554 offset,
555 }
556 }
557}
558
559#[derive(Debug, PartialEq, Eq, Clone)]
564#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
565pub struct Key {
566 pub method: String,
568 pub encryption_key: Option<String>,
570}
571
572impl Key {
573 pub fn new(method: KeyMethod) -> Self {
574 Key {
575 method: method.to_string(),
576 encryption_key: Default::default(),
577 }
578 }
579
580 pub fn with_encryption_key(method: KeyMethod, encryption_key: impl ToString) -> Self {
582 Key {
583 method: method.to_string(),
584 encryption_key: Some(encryption_key.to_string()),
585 }
586 }
587
588 pub fn try_parse_keymethod(&self) -> Result<KeyMethod, ParseEnumError> {
590 KeyMethod::from_str(self.method.as_str())
591 }
592
593 pub fn set_keymethod(&mut self, method: KeyMethod) {
595 self.method = method.to_string();
596 }
597}
598
599impl From<KeyMethod> for Key {
600 fn from(method: KeyMethod) -> Self {
601 Key {
602 method: method.to_string(),
603 encryption_key: None,
604 }
605 }
606}
607
608#[derive(Debug, PartialEq, Eq, Clone)]
612#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
613pub struct Attribute {
614 pub attribute: String,
616 pub value: Option<String>,
618}
619
620impl Attribute {
621 pub fn new(attribute: impl ToString) -> Self {
625 Attribute {
626 attribute: attribute.to_string(),
627 value: Default::default(),
628 }
629 }
630
631 pub fn with_value(attribute: impl ToString, value: impl ToString) -> Self {
633 Attribute {
634 attribute: attribute.to_string(),
635 value: Some(value.to_string()),
636 }
637 }
638}
639
640#[derive(Debug, PartialEq, Eq, Clone)]
644#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
645pub struct Media {
646 pub media: String,
648 pub port: u16,
650 pub num_ports: Option<u16>,
652 pub proto: String,
654 pub fmt: String,
656 pub media_title: Option<String>,
658 pub connections: Vec<Connection>,
660 pub bandwidths: Vec<Bandwidth>,
662 pub key: Option<Key>,
664 pub attributes: Vec<Attribute>,
666}
667
668impl Media {
669 pub fn new(media: MediaType, port: u16, proto: TransportProto, fmt: impl ToString) -> Self {
670 Media {
671 media: media.to_string(),
672 port,
673 num_ports: Default::default(),
674 proto: proto.to_string(),
675 fmt: fmt.to_string(),
676 media_title: Default::default(),
677 connections: Default::default(),
678 bandwidths: Default::default(),
679 key: Default::default(),
680 attributes: Default::default(),
681 }
682 }
683
684 pub fn builder(
685 media: MediaType,
686 port: u16,
687 proto: TransportProto,
688 fmt: impl ToString,
689 ) -> builders::Media {
690 builders::Media::new(media, port, proto, fmt)
691 }
692
693 pub fn set_num_ports(&mut self, num_ports: u16) {
694 self.num_ports = Some(num_ports);
695 }
696
697 pub fn set_media_title(&mut self, media_title: impl ToString) {
698 self.media_title = Some(media_title.to_string());
699 }
700
701 pub fn add_connection(&mut self, connection: impl Into<Connection>) {
702 self.connections.push(connection.into());
703 }
704
705 pub fn add_connections(
706 &mut self,
707 connections: impl IntoIterator<Item = impl Into<Connection>>,
708 ) {
709 self.connections
710 .extend(connections.into_iter().map(|c| c.into()))
711 }
712
713 pub fn add_bandwidth(&mut self, bandwidth: Bandwidth) {
714 self.bandwidths.push(bandwidth);
715 }
716
717 pub fn add_bandwidths(&mut self, bandwidths: impl IntoIterator<Item = Bandwidth>) {
718 self.bandwidths.extend(bandwidths)
719 }
720
721 pub fn set_encryption_key(&mut self, key: impl Into<Key>) {
722 self.key = Some(key.into());
723 }
724
725 pub fn add_attribute(&mut self, attribute: impl Into<Attribute>) {
726 self.attributes.push(attribute.into());
727 }
728
729 pub fn add_attribute_from_str(&mut self, attribute: impl ToString) {
730 self.attributes.push(Attribute::new(attribute));
731 }
732
733 pub fn add_attribute_with_value(&mut self, attribute: impl ToString, value: impl ToString) {
734 self.attributes
735 .push(Attribute::with_value(attribute, value));
736 }
737
738 pub fn add_attributes(&mut self, attributes: impl IntoIterator<Item = impl Into<Attribute>>) {
739 self.attributes
740 .extend(attributes.into_iter().map(|a| a.into()))
741 }
742
743 pub fn add_attributes_from_strs(
744 &mut self,
745 attributes: impl IntoIterator<Item = impl ToString>,
746 ) {
747 self.attributes
748 .extend(attributes.into_iter().map(|a| Attribute::new(a)))
749 }
750
751 pub fn has_attribute(&self, name: &str) -> bool {
755 self.attributes.iter().any(|a| a.attribute == name)
756 }
757
758 pub fn get_first_attribute_value<'a>(&'a self, name: &'a str) -> Option<Option<&'a str>> {
763 self.get_attribute_values(name).next()
764 }
765
766 pub fn get_attribute_values<'a>(
770 &'a self,
771 name: &'a str,
772 ) -> impl Iterator<Item = Option<&'a str>> {
773 self.attributes
774 .iter()
775 .filter(move |a| a.attribute == name)
776 .map(|a| a.value.as_deref())
777 }
778
779 pub fn try_parse_mediatype(&self) -> Result<MediaType, ParseEnumError> {
781 MediaType::from_str(self.media.as_str())
782 }
783
784 pub fn set_mediatype(&mut self, media: MediaType) {
786 self.media = media.to_string();
787 }
788
789 pub fn parse_transport_proto(&self) -> TransportProto {
791 TransportProto::from(self.proto.as_str())
792 }
793
794 pub fn set_transport_proto(&mut self, proto: TransportProto) {
796 self.proto = proto.to_string();
797 }
798
799 pub fn get_first_attribute_typed<T: TypedAttribute>(
801 &self,
802 ) -> Option<Result<T, AttributeError>> {
803 self.attributes_typed().next()
804 }
805
806 pub fn attributes_typed<'a, T: TypedAttribute>(
812 &'a self,
813 ) -> impl Iterator<Item = Result<T, AttributeError>> + 'a {
814 self.attributes
815 .iter()
816 .filter(move |a| a.attribute == T::NAME)
817 .map(|a| {
818 T::from_str(a.value.as_deref().unwrap_or_default())
820 })
821 }
822}
823
824#[derive(Debug, PartialEq, Eq, Clone)]
828#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
829pub struct Session {
830 pub origin: Origin,
832 pub session_name: String,
834 pub session_description: Option<String>,
836 pub uri: Option<String>,
838 pub emails: Vec<String>,
840 pub phones: Vec<String>,
842 pub connection: Option<Connection>,
844 pub bandwidths: Vec<Bandwidth>,
846 pub times: Vec<Time>,
848 pub time_zones: Vec<TimeZone>,
850 pub key: Option<Key>,
852 pub attributes: Vec<Attribute>,
854 pub medias: Vec<Media>,
856}
857
858impl Session {
859 pub fn new(origin: Origin, session_name: impl ToString) -> Self {
860 Session {
861 origin,
862 session_name: session_name.to_string(),
863 session_description: Default::default(),
864 uri: Default::default(),
865 emails: Default::default(),
866 phones: Default::default(),
867 connection: Default::default(),
868 bandwidths: Default::default(),
869 times: Default::default(),
870 time_zones: Default::default(),
871 key: Default::default(),
872 attributes: Default::default(),
873 medias: Default::default(),
874 }
875 }
876
877 pub fn builder(origin: Origin, session_name: impl ToString) -> builders::Session {
878 builders::Session::new(origin, session_name)
879 }
880
881 pub fn set_session_description(&mut self, session_description: impl ToString) {
882 self.session_description = Some(session_description.to_string());
883 }
884
885 pub fn set_uri(&mut self, uri: impl ToString) {
886 self.uri = Some(uri.to_string());
887 }
888
889 pub fn add_email(&mut self, email: impl ToString) {
890 self.emails.push(email.to_string());
891 }
892
893 pub fn add_emails(&mut self, emails: impl IntoIterator<Item = impl ToString>) {
894 self.emails
895 .extend(emails.into_iter().map(|e| e.to_string()));
896 }
897
898 pub fn add_phone(&mut self, phone: impl ToString) {
899 self.phones.push(phone.to_string());
900 }
901
902 pub fn add_phones(&mut self, phones: impl IntoIterator<Item = impl ToString>) {
903 self.phones
904 .extend(phones.into_iter().map(|p| p.to_string()));
905 }
906
907 pub fn set_connection(&mut self, connection: impl Into<Connection>) {
908 self.connection = Some(connection.into());
909 }
910
911 pub fn add_bandwidth(&mut self, bandwidth: Bandwidth) {
912 self.bandwidths.push(bandwidth);
913 }
914
915 pub fn add_bandwidths(&mut self, bandwidths: impl IntoIterator<Item = Bandwidth>) {
916 self.bandwidths.extend(bandwidths);
917 }
918
919 pub fn add_time(&mut self, time: Time) {
920 self.times.push(time);
921 }
922
923 pub fn add_times(&mut self, times: impl IntoIterator<Item = Time>) {
924 self.times.extend(times);
925 }
926
927 pub fn add_time_zone(&mut self, time_zone: TimeZone) {
928 self.time_zones.push(time_zone);
929 }
930
931 pub fn add_time_zones(&mut self, time_zones: impl IntoIterator<Item = TimeZone>) {
932 self.time_zones.extend(time_zones);
933 }
934
935 pub fn set_encryption_key(&mut self, key: impl Into<Key>) {
936 self.key = Some(key.into());
937 }
938
939 pub fn add_attribute(&mut self, attribute: impl Into<Attribute>) {
940 self.attributes.push(attribute.into());
941 }
942
943 pub fn add_attribute_from_str(&mut self, attribute: impl ToString) {
944 self.attributes.push(Attribute::new(attribute));
945 }
946
947 pub fn add_attribute_with_value(&mut self, attribute: impl ToString, value: impl ToString) {
948 self.attributes
949 .push(Attribute::with_value(attribute, value));
950 }
951
952 pub fn add_attributes(&mut self, attributes: impl IntoIterator<Item = impl Into<Attribute>>) {
953 self.attributes
954 .extend(attributes.into_iter().map(|a| a.into()))
955 }
956
957 pub fn add_attributes_from_strs(
958 &mut self,
959 attributes: impl IntoIterator<Item = impl ToString>,
960 ) {
961 self.attributes
962 .extend(attributes.into_iter().map(|a| Attribute::new(a)))
963 }
964
965 pub fn add_media(&mut self, media: Media) {
966 self.medias.push(media);
967 }
968
969 pub fn add_medias(&mut self, medias: impl IntoIterator<Item = Media>) {
970 self.medias.extend(medias)
971 }
972
973 pub fn has_attribute(&self, name: &str) -> bool {
977 self.attributes.iter().any(|a| a.attribute == name)
978 }
979
980 pub fn get_first_attribute_value<'a>(&'a self, name: &'a str) -> Option<Option<&'a str>> {
985 self.get_attribute_values(name).next()
986 }
987
988 pub fn get_attribute_values<'a>(
992 &'a self,
993 name: &'a str,
994 ) -> impl Iterator<Item = Option<&'a str>> {
995 self.attributes
996 .iter()
997 .filter(move |a| a.attribute == name)
998 .map(|a| a.value.as_deref())
999 }
1000
1001 pub fn get_first_attribute_typed<T: TypedAttribute>(
1003 &self,
1004 ) -> Option<Result<T, AttributeError>> {
1005 self.attributes_typed().next()
1006 }
1007
1008 pub fn attributes_typed<'a, T: TypedAttribute>(
1014 &'a self,
1015 ) -> impl Iterator<Item = Result<T, AttributeError>> + 'a {
1016 self.attributes
1017 .iter()
1018 .filter(move |a| a.attribute == T::NAME)
1019 .map(|a| {
1020 T::from_str(a.value.as_deref().unwrap_or_default())
1022 })
1023 }
1024}
1025
1026#[cfg(test)]
1027mod tests {
1028 use std::net::Ipv4Addr;
1029
1030 use super::*;
1031
1032 #[test]
1033 fn parse_write() {
1034 let sdp = "v=0\r
1035o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r
1036s=SDP Seminar\r
1037i=A Seminar on the session description protocol\r
1038u=http://www.example.com/seminars/sdp.pdf\r
1039e=j.doe@example.com (Jane Doe)\r
1040p=+1 617 555-6011\r
1041c=IN IP4 224.2.17.12/127\r
1042b=AS:128\r
1043t=2873397496 2873404696\r
1044r=7d 1h 0 25h\r
1045z=2882844526 -1h 2898848070 0\r
1046k=clear:1234\r
1047a=recvonly\r
1048m=audio 49170 RTP/AVP 0\r
1049a=fmtp:0 0-15\r
1050m=video 51372/2 RTP/AVP 99 97 98\r
1051a=rtpmap:99 h263-1998/90000\r
1052a=fingerprint:sha-256 3A:96:6D:57:B2:C2:C7:61:A0:46:3E:1C:97:39:D3:F7:0A:88:A0:B1:EC:03:FB:10:A5:5D:3A:37:AB:DD:02:AA\r
1053a=extmap:2/sendrecv http://example.com/082005/ext.htm#xmeta short\r
1054";
1055 let parsed = Session::parse(sdp.as_bytes()).unwrap();
1056 let mut written = vec![];
1057 parsed.write(&mut written).unwrap();
1058 assert_eq!(String::from_utf8_lossy(&written), sdp);
1059 assert_eq!(parsed.origin.addrtype, AddrType::Ip4);
1060 assert_ne!(parsed.origin.nettype, NetType::Pstn);
1061 assert_eq!(
1062 parsed.origin.try_parse_unicast_ip_address(),
1063 Ok(IpAddr::V4(std::net::Ipv4Addr::new(10, 47, 16, 5)))
1064 );
1065 assert_eq!(parsed.medias[0].try_parse_mediatype(), Ok(MediaType::Audio));
1066 assert_ne!(
1067 parsed.medias[1].parse_transport_proto(),
1068 TransportProto::RtpSavpf,
1069 );
1070 let f = fallible_iterator::convert(parsed.medias[0].attributes_typed::<Fmtp>())
1071 .collect::<Vec<_>>()
1072 .expect("Valid vector of attributes");
1073 assert_eq!(f.len(), 1);
1074 assert_eq!(f[0].format_specific_params[0].param, "0-15");
1075
1076 let e = fallible_iterator::convert(parsed.medias[1].attributes_typed::<ExtMap>())
1077 .collect::<Vec<_>>()
1078 .expect("Vector of extmap attributes");
1079 assert_eq!(e[0].id, 2);
1080 assert_eq!(e[0].direction, Some(Direction::SendRecv));
1081 assert_eq!(
1082 e[0].uri,
1083 "http://example.com/082005/ext.htm#xmeta".to_string()
1084 );
1085 assert_eq!(e[0].attributes, Some("short".to_string()));
1086
1087 let f = fallible_iterator::convert(parsed.medias[1].attributes_typed::<Fingerprint>())
1088 .collect::<Vec<_>>()
1089 .expect("Vector of fingerprint attributes");
1090
1091 assert_eq!(f[0].hash_func, HashFunc::Sha256);
1092 assert_eq!(f[0].fingerprint[4], 0xB2);
1093 assert_eq!(f[0].fingerprint.last(), Some(&0xAA));
1094 }
1095
1096 #[test]
1097 fn parse_media_attributes() {
1098 let media = Media {
1099 media: "video".into(),
1100 port: 51372,
1101 num_ports: Some(2),
1102 proto: "RTP/AVP".into(),
1103 fmt: "99 100".into(),
1104 media_title: None,
1105 connections: vec![],
1106 bandwidths: vec![],
1107 key: None,
1108 attributes: vec![
1109 Attribute {
1110 attribute: "rtpmap".into(),
1111 value: Some("99 h263-1998/90000".into()),
1112 },
1113 Attribute {
1114 attribute: "rtpmap".into(),
1115 value: Some("100 h264/90000".into()),
1116 },
1117 Attribute {
1118 attribute: "rtpmap".into(),
1119 value: None,
1120 },
1121 Attribute {
1122 attribute: "rtpmap".into(),
1123 value: Some(
1124 RtpMap {
1125 payload_type: 101,
1126 encoding_name: "L16".into(),
1127 clock_rate: 16000,
1128 encoding_params: Some("2".into()),
1129 }
1130 .to_string(),
1131 ),
1132 },
1133 Attribute {
1134 attribute: "fmtp".into(),
1135 value: Some(
1136 Fmtp {
1137 fmt: 100,
1138 format_specific_params: Vec::from([
1139 FmtpParam {
1140 param: "profile-level-id".to_string(),
1141 val: Some("42e016".to_string()),
1142 },
1143 FmtpParam {
1144 param: "max-mbps".to_string(),
1145 val: Some("108000".to_string()),
1146 },
1147 FmtpParam {
1148 param: "max-fs".to_string(),
1149 val: Some("3600".to_string()),
1150 },
1151 ]),
1152 }
1153 .to_string(),
1154 ),
1155 },
1156 Attribute {
1157 attribute: "rtcp".into(),
1158 value: Some(
1159 Rtcp {
1160 port: 53020,
1161 nettype: Some(NetType::In),
1162 addrtype: Some(AddrType::Ip4),
1163 connection_address: Some(
1164 std::net::Ipv4Addr::new(126, 16, 64, 4).to_string(),
1165 ),
1166 }
1167 .to_string(),
1168 ),
1169 },
1170 Attribute {
1171 attribute: "fingerprint".into(),
1172 value: Some(
1173 Fingerprint {
1174 hash_func: HashFunc::Other(("custom").to_string()),
1175 fingerprint: [0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6].to_vec(),
1176 }
1177 .to_string(),
1178 ),
1179 },
1180 ],
1181 };
1182
1183 assert!(media.has_attribute("rtpmap"));
1184 assert!(media.has_attribute("rtcp"));
1185 assert!(!media.has_attribute("foo"));
1186
1187 assert_eq!(
1188 media.get_first_attribute_value("rtpmap"),
1189 Some(Some("99 h263-1998/90000"))
1190 );
1191 assert_eq!(
1192 media.get_first_attribute_value("rtcp"),
1193 Some(Some("53020 IN IP4 126.16.64.4"))
1194 );
1195 assert!(media.get_first_attribute_value("foo").is_none());
1196
1197 assert_eq!(
1198 media.get_attribute_values("rtpmap").collect::<Vec<_>>(),
1199 &[
1200 Some("99 h263-1998/90000"),
1201 Some("100 h264/90000"),
1202 None,
1203 Some("101 L16/16000/2"),
1204 ]
1205 );
1206
1207 let v = media
1208 .attributes_typed::<RtpMap>()
1209 .collect::<Vec<Result<RtpMap, AttributeError>>>();
1210 assert_eq!(v[0].as_ref().unwrap().clock_rate, 90000);
1211 assert_eq!(v[1].as_ref().unwrap().encoding_name, "h264");
1212
1213 let v = media
1214 .attributes_typed::<RtpMap>()
1215 .filter(|attr| {
1216 let Ok(at) = attr else { return false };
1217 at.payload_type == 99
1218 })
1219 .collect::<Vec<_>>();
1220 assert_eq!(v.len(), 1);
1221 assert_eq!(v[0].as_ref().unwrap().encoding_name, "h263-1998");
1222
1223 assert_eq!(
1224 media.get_first_attribute_value("fmtp"),
1225 Some(Some(
1226 "100 profile-level-id=42e016;max-mbps=108000;max-fs=3600"
1227 ))
1228 );
1229
1230 let r = media.attributes_typed::<Rtcp>().collect::<Vec<_>>();
1231 assert_eq!(r[0].as_ref().unwrap().addrtype, Some(AddrType::Ip4));
1232 assert_eq!(r[0].as_ref().unwrap().port, 53020);
1233
1234 let rtcp_attr = media.get_first_attribute_typed::<Rtcp>().unwrap().unwrap();
1235 assert_eq!(rtcp_attr.addrtype, Some(AddrType::Ip4));
1236 assert_eq!(rtcp_attr.port, 53020);
1237
1238 assert_eq!(
1239 media
1240 .get_attribute_values("fingerprint")
1241 .collect::<Vec<_>>(),
1242 &[Some("custom A1:B2:C3:D4:E5:F6")]
1243 );
1244
1245 assert!(media.get_attribute_values("foo").next().is_none());
1246 }
1247
1248 #[test]
1249 fn parse_session_attributes() {
1250 let session = Session {
1251 origin: Origin {
1252 username: Some("jdoe".into()),
1253 sess_id: "2890844526".into(),
1254 sess_version: 2890842807,
1255 nettype: "IN".into(),
1256 addrtype: "IP4".into(),
1257 unicast_address: "10.47.16.5".into(),
1258 },
1259 session_name: "SDP Seminar".into(),
1260 session_description: None,
1261 uri: None,
1262 emails: vec![],
1263 phones: vec![],
1264 connection: None,
1265 bandwidths: vec![],
1266 times: vec![Time {
1267 start_time: 0,
1268 stop_time: 0,
1269 repeats: vec![],
1270 }],
1271 time_zones: vec![],
1272 key: None,
1273 attributes: vec![
1274 Attribute {
1275 attribute: "rtpmap".into(),
1276 value: Some("99 h263-1998/90000".into()),
1277 },
1278 Attribute {
1279 attribute: "rtpmap".into(),
1280 value: Some("100 h264/90000".into()),
1281 },
1282 Attribute {
1283 attribute: "rtpmap".into(),
1284 value: Some(
1285 RtpMap {
1286 payload_type: 101,
1287 encoding_name: "L16".into(),
1288 clock_rate: 16000,
1289 encoding_params: Some("2".into()),
1290 }
1291 .to_string(),
1292 ),
1293 },
1294 Attribute {
1295 attribute: "rtcp".into(),
1296 value: None,
1297 },
1298 Attribute {
1299 attribute: "extmap".into(),
1300 value: Some(
1301 ExtMap {
1302 id: 1,
1303 direction: None,
1304 uri: "URI-toffset".to_string(),
1305 attributes: None,
1306 }
1307 .to_string(),
1308 ),
1309 },
1310 ],
1311 medias: vec![],
1312 };
1313
1314 assert!(session.has_attribute("rtpmap"));
1315 assert!(session.has_attribute("rtcp"));
1316 assert!(!session.has_attribute("foo"));
1317
1318 assert_eq!(
1319 session.get_first_attribute_value("rtpmap"),
1320 Some(Some("99 h263-1998/90000"))
1321 );
1322
1323 let v = session
1324 .get_first_attribute_value("rtpmap")
1325 .unwrap()
1326 .unwrap();
1327 let rtpmap = RtpMap::from_str(v).unwrap();
1328 assert_eq!(rtpmap.clock_rate, 90000);
1329 assert_eq!(rtpmap.encoding_name, "h263-1998");
1330 assert_ne!(rtpmap.encoding_name, "h263");
1331 assert_ne!(rtpmap.encoding_params, Some("2".to_string()));
1332 assert_eq!(rtpmap.payload_type, 99);
1333
1334 let rtpmap = session
1335 .get_first_attribute_typed::<RtpMap>()
1336 .unwrap()
1337 .unwrap();
1338 assert_eq!(rtpmap.clock_rate, 90000);
1339 assert_eq!(rtpmap.encoding_name, "h263-1998");
1340 assert_ne!(rtpmap.encoding_name, "h263");
1341 assert_ne!(rtpmap.encoding_params, Some("2".to_string()));
1342 assert_eq!(rtpmap.payload_type, 99);
1343
1344 assert_eq!(session.get_first_attribute_value("rtcp"), Some(None));
1345
1346 assert!(session.get_first_attribute_value("foo").is_none());
1347
1348 assert_eq!(
1349 session.get_attribute_values("rtpmap").collect::<Vec<_>>(),
1350 &[
1351 Some("99 h263-1998/90000"),
1352 Some("100 h264/90000"),
1353 Some("101 L16/16000/2")
1354 ]
1355 );
1356 assert_eq!(
1357 session.get_attribute_values("rtcp").collect::<Vec<_>>(),
1358 &[None]
1359 );
1360
1361 assert_eq!(
1362 session.get_attribute_values("extmap").collect::<Vec<_>>(),
1363 &[Some("1 URI-toffset")]
1364 );
1365
1366 assert!(session.get_attribute_values("foo").next().is_none());
1367
1368 let a = fallible_iterator::convert(session.attributes_typed::<RtpMap>())
1369 .collect::<Vec<_>>()
1370 .expect("Valid vector of attributes");
1371 assert_eq!(a[2].encoding_name, "L16");
1372 assert_eq!(a[0].payload_type, 99);
1373 }
1374
1375 #[test]
1376 fn origin_parse_address_error() {
1377 use std::net::Ipv6Addr;
1378
1379 let mut origin = Origin {
1380 username: None,
1381 sess_id: "1234".to_string(),
1382 sess_version: 0,
1383 nettype: NetType::In,
1384 addrtype: AddrType::Ip4,
1385 unicast_address: "127.0.0.1".to_string(),
1386 };
1387
1388 assert_eq!(origin.addrtype, AddrType::Ip4);
1389 assert_eq!(
1390 origin.try_parse_unicast_ip_address().unwrap(),
1391 IpAddr::V4(Ipv4Addr::LOCALHOST),
1392 );
1393
1394 origin.unicast_address = "127.0.0.1/24".to_string();
1395 assert_eq!(
1396 origin.try_parse_unicast_ip_address().unwrap_err(),
1397 origin.unicast_address
1398 );
1399
1400 origin.unicast_address = "non-ip".to_string();
1401 assert_eq!(
1402 origin.try_parse_unicast_ip_address().unwrap_err(),
1403 origin.unicast_address
1404 );
1405
1406 origin.set_unicast_ip_address(Ipv6Addr::LOCALHOST);
1407 assert_eq!(origin.addrtype, AddrType::Ip6);
1408 assert_eq!(
1409 origin.try_parse_unicast_ip_address().unwrap(),
1410 IpAddr::V6(Ipv6Addr::LOCALHOST),
1411 );
1412 }
1413
1414 #[test]
1415 #[allow(deprecated)]
1416 fn connection_parse_address_error() {
1417 use std::net::Ipv6Addr;
1418
1419 let mut connection = Connection {
1420 nettype: NetType::In,
1421 addrtype: AddrType::Ip4,
1422 connection_address: "127.0.0.1".to_string(),
1423 };
1424
1425 assert_eq!(connection.addrtype, AddrType::Ip4);
1426 assert_eq!(
1427 connection.try_parse_connection_ip_address().unwrap(),
1428 IpAddr::V4(Ipv4Addr::LOCALHOST),
1429 );
1430
1431 connection.connection_address = "127.0.0.1/24".to_string();
1433 assert_eq!(
1434 connection.try_parse_connection_ip_address().unwrap(),
1435 IpAddr::V4(Ipv4Addr::LOCALHOST),
1436 );
1437
1438 connection.connection_address = "non-ip".to_string();
1439 assert_eq!(
1440 connection.try_parse_connection_ip_address().unwrap_err(),
1441 connection.connection_address
1442 );
1443
1444 connection.set_connection_ip_address(Ipv6Addr::LOCALHOST);
1445 assert_eq!(connection.addrtype, AddrType::Ip6);
1446 assert_eq!(
1447 connection.try_parse_connection_ip_address().unwrap(),
1448 IpAddr::V6(Ipv6Addr::LOCALHOST),
1449 );
1450 }
1451
1452 #[test]
1453 fn connection_try_is_multicast() {
1454 let connection = Connection::new(NetType::In, AddrType::Ip4, "224.2.1.1");
1455 assert_eq!(connection.try_is_multicast(), Ok(true));
1456
1457 let connection = Connection::new(NetType::In, AddrType::Ip4, "127.0.0.1");
1458 assert_eq!(connection.try_is_multicast(), Ok(false));
1459
1460 let connection = Connection::new(NetType::In, AddrType::Ip4, "224.2.1.1/x");
1462 assert_eq!(connection.try_is_multicast(), Ok(true));
1463
1464 let connection = Connection::new(NetType::In, AddrType::Ip4, "x/16");
1465 assert_eq!(
1466 connection.try_is_multicast().unwrap_err(),
1467 ConnectionAddressError::InvalidAddress("x/16".into())
1468 );
1469 }
1470
1471 #[test]
1472 fn connection_is_ipv4_ipv6() {
1473 let connection = Connection::new(NetType::In, AddrType::Ip4, "224.2.1.1/16");
1474 assert!(connection.is_ipv4());
1475 assert!(!connection.is_ipv6());
1476
1477 let connection = Connection::new(NetType::In, AddrType::Ip6, "ff12::5/3");
1478 assert!(connection.is_ipv6());
1479 assert!(!connection.is_ipv4());
1480
1481 let connection = Connection::new(NetType::In, AddrType::Ip4, "x/16");
1482 assert!(!connection.is_ipv4());
1483 assert!(!connection.is_ipv6());
1484 }
1485
1486 #[test]
1487 fn connection_try_address() {
1488 let connection = Connection::new(NetType::In, AddrType::Ip4, "224.2.1.1/16/2");
1489 assert_eq!(
1490 connection.try_address().unwrap(),
1491 ConnectionAddress {
1492 ip: "224.2.1.1".parse().unwrap(),
1493 ttl: Some(16),
1494 count: Some(2),
1495 }
1496 );
1497
1498 let connection = Connection::new(NetType::In, AddrType::Ip4, "224.2.1.1/16");
1499 assert_eq!(
1500 connection.try_address().unwrap(),
1501 ConnectionAddress {
1502 ip: "224.2.1.1".parse().unwrap(),
1503 ttl: Some(16),
1504 count: None,
1505 }
1506 );
1507
1508 let connection = Connection::new(NetType::In, AddrType::Ip4, "127.0.0.1");
1509 assert_eq!(
1510 connection.try_address().unwrap(),
1511 ConnectionAddress {
1512 ip: "127.0.0.1".parse().unwrap(),
1513 ttl: None,
1514 count: None,
1515 }
1516 );
1517
1518 let connection = Connection::new(NetType::In, AddrType::Ip6, "ff12::5/3");
1520 assert_eq!(
1521 connection.try_address().unwrap(),
1522 ConnectionAddress {
1523 ip: "ff12::5".parse().unwrap(),
1524 ttl: None,
1525 count: Some(3),
1526 }
1527 );
1528
1529 let connection = Connection::new(NetType::In, AddrType::Ip4, "x/16");
1530 assert_eq!(
1531 connection.try_address().unwrap_err(),
1532 ConnectionAddressError::InvalidAddress("x/16".into())
1533 );
1534
1535 let connection = Connection::new(NetType::In, AddrType::Ip4, "224.2.1.1/x");
1536 assert_eq!(
1537 connection.try_address().unwrap_err(),
1538 ConnectionAddressError::InvalidTtl("224.2.1.1/x".into())
1539 );
1540
1541 let connection = Connection::new(NetType::In, AddrType::Ip4, "224.2.1.1/300");
1542 assert_eq!(
1543 connection.try_address().unwrap_err(),
1544 ConnectionAddressError::InvalidTtl("224.2.1.1/300".into())
1545 );
1546
1547 let connection = Connection::new(NetType::In, AddrType::Ip4, "224.2.1.1/16/x");
1548 assert_eq!(
1549 connection.try_address().unwrap_err(),
1550 ConnectionAddressError::InvalidCount("224.2.1.1/16/x".into())
1551 );
1552 }
1553
1554 #[test]
1555 fn connection_from_ip_addr_ttl() {
1556 use std::net::Ipv6Addr;
1557
1558 let connection = Connection::from_ip_addr_ttl(Ipv4Addr::new(224, 2, 1, 1), Some(16));
1559 assert_eq!(connection.nettype, NetType::In);
1560 assert_eq!(connection.addrtype, AddrType::Ip4);
1561 assert_eq!(connection.connection_address, "224.2.1.1/16");
1562
1563 let connection =
1566 Connection::from_ip_addr_ttl(Ipv6Addr::new(0xff12, 0, 0, 0, 0, 0, 0, 5), Some(16));
1567 assert_eq!(connection.addrtype, AddrType::Ip6);
1568 assert_eq!(connection.connection_address, "ff12::5");
1569
1570 let connection = Connection::from_ip_addr_ttl(Ipv4Addr::new(224, 2, 1, 1), None);
1571 assert_eq!(connection.connection_address, "224.2.1.1");
1572 }
1573
1574 #[test]
1575 fn attribute_lookup_case_sensitive() {
1576 let sdp = b"v=0\r
1577o=- 0 0 IN IP4 0.0.0.0\r
1578s=x\r
1579t=0 0\r
1580a=Foo:Bar\r
1581m=audio 49170 RTP/AVP 0\r
1582a=RTPMAP:0 pcma/8000\r
1583";
1584 let session = Session::parse(&sdp[..]).unwrap();
1585
1586 assert_eq!(session.attributes[0].attribute, "Foo");
1589 assert!(session.has_attribute("Foo"));
1590 assert!(!session.has_attribute("foo"));
1591 assert!(!session.has_attribute("FOO"));
1592 assert_eq!(session.get_first_attribute_value("Foo"), Some(Some("Bar")));
1593 assert_eq!(session.get_first_attribute_value("FOO"), None);
1594
1595 let media = &session.medias[0];
1596 assert!(!media.has_attribute("rtpmap"));
1597 assert!(media.has_attribute("RTPMAP"));
1598 assert!(media.get_attribute_values("rtpmap").next().is_none());
1599 assert_eq!(
1600 media.get_attribute_values("RTPMAP").collect::<Vec<_>>(),
1601 vec![Some("0 pcma/8000")]
1602 );
1603 }
1604}