/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![warn(clippy::all)] #![forbid(unsafe_code)]
#[macro_use] externcrate log; #[cfg(feature = "serialize")] #[macro_use] externcrate serde_derive; #[cfg(feature = "serialize")] externcrate serde; use std::convert::TryFrom; use std::fmt;
#[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pubenum SdpType { // Note: Email, Information, Key, Phone, Repeat, Uri and Zone are left out // on purposes as we don't want to support them.
Attribute(SdpAttribute),
Bandwidth(SdpBandwidth),
Connection(SdpConnection),
Media(SdpMediaLine),
Origin(SdpOrigin),
Session(String),
Timing(SdpTiming),
Version(u64),
}
/* removing this wrap would not allow us to call this from the match statement inside
* parse_sdp_line() */ #[allow(clippy::unnecessary_wraps)] fn parse_session(value: &str) -> Result<SdpType, SdpParserInternalError> {
trace!("session: {}", value);
Ok(SdpType::Session(String::from(value)))
}
fn parse_version(value: &str) -> Result<SdpType, SdpParserInternalError> { let ver = value.parse::<u64>()?; if ver != 0 { return Err(SdpParserInternalError::Generic(format!( "version type contains unsupported value {ver}"
)));
};
trace!("version: {}", ver);
Ok(SdpType::Version(ver))
}
fn parse_origin(value: &str) -> Result<SdpType, SdpParserInternalError> { letmut tokens = value.split_whitespace(); let username = match tokens.next() {
None => { return Err(SdpParserInternalError::Generic( "Origin type is missing username token".to_string(),
));
}
Some(x) => x,
}; let session_id = match tokens.next() {
None => { return Err(SdpParserInternalError::Generic( "Origin type is missing session ID token".to_string(),
));
}
Some(x) => x.parse::<u64>()?,
}; let session_version = match tokens.next() {
None => { return Err(SdpParserInternalError::Generic( "Origin type is missing session version token".to_string(),
));
}
Some(x) => x.parse::<u64>()?,
}; match tokens.next() {
None => { return Err(SdpParserInternalError::Generic( "Origin type is missing network type token".to_string(),
));
}
Some(x) => parse_network_type(x)?,
}; let addrtype = match tokens.next() {
None => { return Err(SdpParserInternalError::Generic( "Origin type is missing address type token".to_string(),
));
}
Some(x) => parse_address_type(x)?,
}; let unicast_addr = match tokens.next() {
None => { return Err(SdpParserInternalError::Generic( "Origin type is missing IP address token".to_string(),
));
}
Some(x) => ExplicitlyTypedAddress::try_from((addrtype, x))?,
}; if addrtype != unicast_addr.address_type() { return Err(SdpParserInternalError::Generic( "Origin addrtype does not match address.".to_string(),
));
} let o = SdpOrigin {
username: String::from(username),
session_id,
session_version,
unicast_addr,
};
trace!("origin: {}", o);
Ok(SdpType::Origin(o))
}
fn parse_connection(value: &str) -> Result<SdpType, SdpParserInternalError> { let cv: Vec<&str> = value.split_whitespace().collect(); if cv.len() != 3 { return Err(SdpParserInternalError::Generic( "connection attribute must have three tokens".to_string(),
));
}
parse_network_type(cv[0])?; let addrtype = parse_address_type(cv[1])?; letmut ttl = None; letmut amount = None; letmut addr_token = cv[2]; if addr_token.find('/').is_some() { let addr_tokens: Vec<&str> = addr_token.split('/').collect(); if addr_tokens.len() >= 3 {
amount = Some(addr_tokens[2].parse::<u32>()?);
}
ttl = Some(addr_tokens[1].parse::<u8>()?);
addr_token = addr_tokens[0];
} let address = ExplicitlyTypedAddress::try_from((addrtype, addr_token))?; let c = SdpConnection {
address,
ttl,
amount,
};
trace!("connection: {}", c);
Ok(SdpType::Connection(c))
}
fn parse_bandwidth(value: &str) -> Result<SdpType, SdpParserInternalError> { let bv: Vec<&str> = value.split(':').collect(); if bv.len() != 2 { return Err(SdpParserInternalError::Generic( "bandwidth attribute must have two tokens".to_string(),
));
} let bandwidth = bv[1].parse::<u32>()?; let bw = match bv[0].to_uppercase().as_ref() { "AS" => SdpBandwidth::As(bandwidth), "CT" => SdpBandwidth::Ct(bandwidth), "TIAS" => SdpBandwidth::Tias(bandwidth),
_ => SdpBandwidth::Unknown(String::from(bv[0]), bandwidth),
};
trace!("bandwidth: {}", bw);
Ok(SdpType::Bandwidth(bw))
}
fn parse_timing(value: &str) -> Result<SdpType, SdpParserInternalError> { let tv: Vec<&str> = value.split_whitespace().collect(); if tv.len() != 2 { return Err(SdpParserInternalError::Generic( "timing attribute must have two tokens".to_string(),
));
} let start = tv[0].parse::<u64>()?; let stop = tv[1].parse::<u64>()?; let t = SdpTiming { start, stop };
trace!("timing: {}", t);
Ok(SdpType::Timing(t))
}
pubfn parse_sdp_line(line: &str, line_number: usize) -> Result<SdpLine, SdpParserError> { if line.find('=').is_none() { return Err(SdpParserError::Line {
error: SdpParserInternalError::Generic("missing = character in line".to_string()),
line: line.to_string(),
line_number,
});
} letmut splitted_line = line.splitn(2, '='); let line_type = match splitted_line.next() {
None => { return Err(SdpParserError::Line {
error: SdpParserInternalError::Generic("missing type".to_string()),
line: line.to_string(),
line_number,
});
}
Some(t) => { let trimmed = t.trim(); if trimmed.len() > 1 { return Err(SdpParserError::Line {
error: SdpParserInternalError::Generic("type too long".to_string()),
line: line.to_string(),
line_number,
});
} if trimmed.is_empty() { return Err(SdpParserError::Line {
error: SdpParserInternalError::Generic("type is empty".to_string()),
line: line.to_string(),
line_number,
});
}
trimmed.to_lowercase()
}
}; let (line_value, untrimmed_line_value) = match splitted_line.next() {
None => { return Err(SdpParserError::Line {
error: SdpParserInternalError::Generic("missing value".to_string()),
line: line.to_string(),
line_number,
});
}
Some(v) => { let trimmed = v.trim(); // For compatibility with sites that don't adhere to "s=-" for no session ID if trimmed.is_empty() && line_type.as_str() != "s" { return Err(SdpParserError::Line {
error: SdpParserInternalError::Generic("value is empty".to_string()),
line: line.to_string(),
line_number,
});
}
(trimmed, v)
}
}; match line_type.as_ref() { "a" => parse_attribute(line_value), "b" => parse_bandwidth(line_value), "c" => parse_connection(line_value), "e" => Err(SdpParserInternalError::Generic(format!( "unsupported type email: {line_value}"
))), "i" => Err(SdpParserInternalError::Generic(format!( "unsupported type information: {line_value}"
))), "k" => Err(SdpParserInternalError::Generic(format!( "unsupported insecure key exchange: {line_value}"
))), "m" => parse_media(line_value), "o" => parse_origin(line_value), "p" => Err(SdpParserInternalError::Generic(format!( "unsupported type phone: {line_value}"
))), "r" => Err(SdpParserInternalError::Generic(format!( "unsupported type repeat: {line_value}"
))), "s" => parse_session(untrimmed_line_value), "t" => parse_timing(line_value), "u" => Err(SdpParserInternalError::Generic(format!( "unsupported type uri: {line_value}"
))), "v" => parse_version(line_value), "z" => Err(SdpParserInternalError::Generic(format!( "unsupported type zone: {line_value}"
))),
_ => Err(SdpParserInternalError::Generic( "unknown sdp type".to_string(),
)),
}
.map(|sdp_type| SdpLine {
line_number,
sdp_type,
text: line.to_owned(),
})
.map_err(|e| match e {
SdpParserInternalError::UnknownAddressType(..)
| SdpParserInternalError::AddressTypeMismatch { .. }
| SdpParserInternalError::Generic(..)
| SdpParserInternalError::Integer(..)
| SdpParserInternalError::Float(..)
| SdpParserInternalError::Domain(..)
| SdpParserInternalError::IpAddress(..) => SdpParserError::Line {
error: e,
line: line.to_string(),
line_number,
},
SdpParserInternalError::Unsupported(..) => SdpParserError::Unsupported {
error: e,
line: line.to_string(),
line_number,
},
})
}
if session.timing.is_none() { return Err(make_seq_error("Missing timing type at session level"));
} // Checks that all media have connections if there is no top level // This explicitly allows for zero connection lines if there are no media // sections for interoperability reasons. let media_cons = &session.media.iter().all(|m| m.get_connection().is_some()); if !media_cons && session.get_connection().is_none() { return Err(make_seq_error( "Without connection type at session level all media sections must have connection types",
));
}
// Check that extmaps are not defined on session and media level if session.get_attribute(SdpAttributeType::Extmap).is_some() { for msection in &session.media { if msection.get_attribute(SdpAttributeType::Extmap).is_some() { return Err(make_seq_error( "Extmap can't be define at session and media level",
));
}
}
}
for msection in &session.media { if msection
.get_attribute(SdpAttributeType::RtcpMuxOnly)
.is_some()
&& msection.get_attribute(SdpAttributeType::RtcpMux).is_none()
{ return Err(make_seq_error( "rtcp-mux-only media sections must also contain the rtcp-mux attribute",
));
}
let rids: Vec<&SdpAttributeRid> = msection
.get_attributes()
.iter()
.filter_map(|attr| match *attr {
SdpAttribute::Rid(ref rid) => Some(rid),
_ => None,
})
.collect(); let recv_rids: Vec<&str> = rids
.iter()
.filter_map(|rid| match rid.direction {
SdpSingleDirection::Recv => Some(rid.id.as_str()),
_ => None,
})
.collect(); let send_rids: Vec<&str> = rids
.iter()
.filter_map(|rid| match rid.direction {
SdpSingleDirection::Send => Some(rid.id.as_str()),
_ => None,
})
.collect();
for rid_format in rids.iter().flat_map(|rid| &rid.formats) { match *msection.get_formats() {
SdpFormatList::Integers(ref int_fmt) => { if !int_fmt.contains(&(u32::from(*rid_format))) { return Err(make_seq_error( "Rid pts must be declared in the media section",
));
}
}
SdpFormatList::Strings(ref str_fmt) => { if !str_fmt.contains(&rid_format.to_string()) { return Err(make_seq_error( "Rid pts must be declared in the media section",
));
}
}
}
}
iflet Some(SdpAttribute::Simulcast(simulcast)) =
msection.get_attribute(SdpAttributeType::Simulcast)
{ let check_defined_rids =
|simulcast_version_list: &Vec<SdpAttributeSimulcastVersion>,
rid_ids: &[&str]|
-> Result<(), SdpParserError> { for simulcast_rid in simulcast_version_list.iter().flat_map(|x| &x.ids) { if !rid_ids.contains(&simulcast_rid.id.as_str()) { return Err(make_seq_error( "Simulcast RIDs must be defined in any rid attribute",
));
}
}
Ok(())
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.