Bug 1566701 - P1 - Update rsdparsa to HEAD of mozilla/rsdparsa with FQDN support - r=drno

Differential Revision: https://phabricator.services.mozilla.com/D38564

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Nico Grunbaum 2019-07-23 21:55:13 +00:00
parent d37254f9ca
commit d6d546de5b
16 changed files with 996 additions and 364 deletions

View File

@ -0,0 +1,33 @@
# Changelog
## [Unreleased]
- Added support for FQDN addresses
## [0.2.2] - 2019-06-21
### Changed
- Minimum Rust version >= 1.35
## [0.2.0] - 2019-06-15
### Changed
- Minimum Rust version >= 1.30.0
- Changed code coverage from kcov to tarpaulin
- Moved file parser example to examples sub directory
- Replaced cause() with source() in unit test
- Moved all unit tests into tests modules
### Fixed
- Unknown extensions in candidate attributes (#103)
- Reduced amount of internal clone() calls significantly
- Added dyn to error:Error impl required by more recent rust versions
### Added
- Support for anonymization to enable logging of SDP without personal
information
- Quite a bit more unit testing got added
### Removed
- Replaced unsupported types with errors directly in lib.rs
## [0.1.0] - 2019-01-26
- Initial release
- Minimum Rust version >= 1.17.0

View File

@ -0,0 +1,8 @@
# Community Participation Guidelines
This repository is governed by Mozilla's code of conduct and etiquette guidelines.
For more details, please read the
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
## How to Report
For more information on how to report violations of the Community Participation Guidelines, please read our [How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/) page.

View File

@ -0,0 +1,103 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "cfg-if"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ryu"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.15.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "webrtc-sdp"
version = "0.1.0"
dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
"checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4"
"checksum serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "58fc82bec244f168b23d1963b45c8bf5726e9a15a9d146a067f9081aeed2de79"
"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
"checksum syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)" = "846620ec526c1599c070eff393bfeeeb88a93afa2513fc3b49f1fea84cf7b0ed"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"

View File

@ -1,9 +1,9 @@
[package]
name = "webrtc-sdp"
version = "0.1.0"
version = "0.2.2"
authors = ["Nils Ohlmeier <github@ohlmeier.org>"]
description = "This create parses strings in the format of the Session Description Protocol according to RFC4566. It specifically supports the subset of features required to support WebRTC according to the JSEP draft."
homepage = "https://github.com/nils-ohlmeier/rsdparsa"
repository = "https://github.com/mozilla/webrtc-sdp"
readme = "README.md"
keywords = ["webrtc", "sdp", "jsep"]
categories = ["parsing", "network-programming"]
@ -22,6 +22,8 @@ serialize = ["serde", "serde_derive"]
log = {version = "0.4.6"}
serde = {version = "1.0" , optional = true}
serde_derive = {version = "1.0" , optional = true}
url = {version="1.7.2"}
[dev-dependencies]
serde_json = {version = "1.0"}
enum-display-derive = "0.1.0"

View File

@ -1,16 +1,16 @@
# webrtc-sdp
[![Crates.io](https://img.shields.io/crates/v/webrtc-sdp.svg)](https://crates.io/crates/webrtc-sdp)
[![Build Status](https://travis-ci.org/nils-ohlmeier/rsdparsa.svg?branch=master)](https://travis-ci.org/nils-ohlmeier/rsdparsa)
[![Codecov coverage status](https://codecov.io/gh/nils-ohlmeier/rsdparsa/branch/master/graph/badge.svg)](https://codecov.io/gh/nils-ohlmeier/rsdparsa)
[![Build Status](https://travis-ci.org/mozilla/webrtc-sdp.svg?branch=master)](https://travis-ci.org/mozilla/webrtc-sdp)
[![Codecov coverage status](https://codecov.io/gh/mozilla/webrtc-sdp/branch/master/graph/badge.svg)](https://codecov.io/gh/webrtc-sdp/webrtc-sdp)
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](#License)
[![dependency status](https://deps.rs/repo/github/nils-ohlmeier/rsdparsa/status.svg)](https://deps.rs/repo/github/nils-ohlmeier/rsdparsa)
[![dependency status](https://deps.rs/repo/github/mozilla/webrtc-sdp/status.svg)](https://deps.rs/repo/github/mozilla/webrtc-sdp)
A SDP parser written in Rust specifically aimed to handle WebRTC SDP offers and answers.
## Dependecies
* Rust >= 1.30.0
* Rust >= 1.35.0
* log module
* serde module
* serde-derive module
@ -30,11 +30,11 @@ The `sdp` parameter is the string which will get parsed. The `fail_on_warning` p
Warnings will be for example unknown parameters in attributes. Setting `fail_on_warning` to `true` makes most sense during development, when you want to be aware of all potential problems. In production `fail_on_warning` is expected to be `false`.
`parse_sdp()` returns either an `SdpSession` struct ([code](https://github.com/nils-ohlmeier/rsdparsa/blob/master/src/lib.rs#L137)) which contains all the parsed information. Or in case a fatal error was encountered (or if `fail_on_warning` was set to `true` and any warnings were encountered) an `SdpParserError` ([code](https://github.com/nils-ohlmeier/rsdparsa/blob/master/src/error.rs#L117)) will be returned as a `Result`.
`parse_sdp()` returns either an `SdpSession` struct ([code](https://github.com/mozilla/webrtc-sdp/blob/master/src/lib.rs#L137)) which contains all the parsed information. Or in case a fatal error was encountered (or if `fail_on_warning` was set to `true` and any warnings were encountered) an `SdpParserError` ([code](https://github.com/mozilla/webrtc-sdp/blob/master/src/error.rs#L117)) will be returned as a `Result`.
## Examples
The [file parser](https://github.com/nils-ohlmeier/rsdparsa/blob/master/examples/file_parser.rs) in the webrtc-sdp package gives you an easy example of how to invoke the webrtc-sdp parser.
The [file parser](https://github.com/mozilla/webrtc-sdp/blob/master/examples/file_parser.rs) in the webrtc-sdp package gives you an easy example of how to invoke the webrtc-sdp parser.
## Contributing
@ -48,7 +48,7 @@ rustup component add clippy
Check with clippy for warnings in the code:
```
cargo clippy --all-targets --all-features
cargo clippy
```
And format all of the code according to Rust code style convention:
@ -56,6 +56,18 @@ And format all of the code according to Rust code style convention:
cargo fmt --all
```
## Fuzzing
Install cargo-fuzz like this:
```
cargo install cargo-fuzz
```
With rust nightly you can start fuzzing like this:
```
cargo fuzz run fuzz_target_parse_sdp
```
## License
Licensed under [MPL-2.0](https://www.mozilla.org/MPL/2.0/)

View File

@ -0,0 +1,4 @@
target
corpus
artifacts

View File

@ -0,0 +1,22 @@
[package]
name = "webrtc-sdp-fuzz"
version = "0.0.1"
authors = ["Automatically generated"]
publish = false
[package.metadata]
cargo-fuzz = true
[dependencies.webrtc-sdp]
path = ".."
[dependencies.libfuzzer-sys]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "fuzz_target_parse_sdp"
path = "fuzz_targets/fuzz_target_parse_sdp.rs"

View File

@ -0,0 +1,9 @@
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate webrtc_sdp;
fuzz_target!(|data: &[u8]| {
if let Ok(s) = std::str::from_utf8(data) {
let _ = webrtc_sdp::parse_sdp(s, true);
}
});

View File

@ -0,0 +1,257 @@
extern crate url;
use self::url::Host;
use error::SdpParserInternalError;
use std::convert::TryFrom;
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum Address {
Fqdn(String),
Ip(IpAddr),
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Address::Fqdn(fqdn) => fqdn.fmt(f),
Address::Ip(ip) => ip.fmt(f),
}
}
}
impl FromStr for Address {
type Err = SdpParserInternalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut e: Option<SdpParserInternalError> = None;
if s.find(':').is_some() {
match IpAddr::from_str(s) {
Ok(ip) => return Ok(Address::Ip(ip)),
Err(err) => e = Some(err.into()),
}
}
Host::parse(s)
.and_then(|host| match host {
Host::Domain(s) => Ok(Address::Fqdn(s)),
Host::Ipv4(ip) => Ok(Address::Ip(IpAddr::V4(ip))),
Host::Ipv6(ip) => Ok(Address::Ip(IpAddr::V6(ip))),
})
.map_err(|err| e.unwrap_or_else(|| err.into()))
}
}
impl From<ExplicitlyTypedAddress> for Address {
fn from(item: ExplicitlyTypedAddress) -> Self {
match item {
ExplicitlyTypedAddress::Fqdn { domain, .. } => Address::Fqdn(domain),
ExplicitlyTypedAddress::Ip(ip) => Address::Ip(ip),
}
}
}
impl PartialEq for Address {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Address::Fqdn(a), Address::Fqdn(b)) => a.to_lowercase() == b.to_lowercase(),
(Address::Ip(a), Address::Ip(b)) => a == b,
(_, _) => false,
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum AddressType {
IpV4 = 4,
IpV6 = 6,
}
impl fmt::Display for AddressType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AddressType::IpV4 => "IP4",
AddressType::IpV6 => "IP6",
}
.fmt(f)
}
}
impl FromStr for AddressType {
type Err = SdpParserInternalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"IP4" => Ok(AddressType::IpV4),
"IP6" => Ok(AddressType::IpV6),
_ => Err(SdpParserInternalError::UnknownAddressType(s.to_owned())),
}
}
}
pub trait AddressTyped {
fn address_type(&self) -> AddressType;
}
impl AddressTyped for IpAddr {
fn address_type(&self) -> AddressType {
match self {
IpAddr::V4(_) => AddressType::IpV4,
IpAddr::V6(_) => AddressType::IpV6,
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum ExplicitlyTypedAddress {
Fqdn {
address_type: AddressType,
domain: String,
},
Ip(IpAddr),
}
impl fmt::Display for ExplicitlyTypedAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IN {} ", self.address_type())?;
match self {
ExplicitlyTypedAddress::Fqdn { domain, .. } => domain.fmt(f),
ExplicitlyTypedAddress::Ip(ip) => ip.fmt(f),
}
}
}
impl AddressTyped for ExplicitlyTypedAddress {
fn address_type(&self) -> AddressType {
match self {
ExplicitlyTypedAddress::Fqdn { address_type, .. } => *address_type,
ExplicitlyTypedAddress::Ip(ip) => ip.address_type(),
}
}
}
impl From<IpAddr> for ExplicitlyTypedAddress {
fn from(item: IpAddr) -> Self {
ExplicitlyTypedAddress::Ip(item)
}
}
impl From<Ipv4Addr> for ExplicitlyTypedAddress {
fn from(item: Ipv4Addr) -> Self {
ExplicitlyTypedAddress::Ip(IpAddr::V4(item))
}
}
impl From<Ipv6Addr> for ExplicitlyTypedAddress {
fn from(item: Ipv6Addr) -> Self {
ExplicitlyTypedAddress::Ip(IpAddr::V6(item))
}
}
impl TryFrom<(AddressType, &str)> for ExplicitlyTypedAddress {
type Error = SdpParserInternalError;
fn try_from(item: (AddressType, &str)) -> Result<Self, Self::Error> {
match Address::from_str(item.1)? {
Address::Ip(ip) => {
if ip.address_type() != item.0 {
Err(SdpParserInternalError::AddressTypeMismatch {
found: ip.address_type(),
expected: item.0,
})
} else {
Ok(ExplicitlyTypedAddress::Ip(ip))
}
}
Address::Fqdn(domain) => Ok(ExplicitlyTypedAddress::Fqdn {
address_type: item.0,
domain,
}),
}
}
}
impl PartialEq for ExplicitlyTypedAddress {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
ExplicitlyTypedAddress::Fqdn {
address_type: a1,
domain: d1,
},
ExplicitlyTypedAddress::Fqdn {
address_type: a2,
domain: d2,
},
) => a1 == a2 && d1.to_lowercase() == d2.to_lowercase(),
(ExplicitlyTypedAddress::Ip(a), ExplicitlyTypedAddress::Ip(b)) => a == b,
(_, _) => false,
}
}
}
#[cfg(test)]
mod tests {
use self::url::ParseError;
use super::*;
use std::error::Error;
use std::fmt::Display;
use std::net::{AddrParseError, Ipv4Addr, Ipv6Addr};
#[derive(Debug, enum_display_derive::Display)]
enum ParseTestError {
Host(ParseError),
Ip(AddrParseError),
}
impl From<ParseError> for ParseTestError {
fn from(err: ParseError) -> Self {
ParseTestError::Host(err)
}
}
impl From<AddrParseError> for ParseTestError {
fn from(err: AddrParseError) -> Self {
ParseTestError::Ip(err)
}
}
impl Error for ParseTestError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
// Generic error, underlying cause isn't tracked.
match self {
ParseTestError::Host(a) => Some(a),
ParseTestError::Ip(a) => Some(a),
}
}
}
#[test]
fn test_domain_name_parsing() -> Result<(), ParseTestError> {
let address = Host::parse("this.is.a.fqdn")?;
if let Host::Domain(domain) = address {
assert_eq!(domain, "this.is.a.fqdn");
} else {
panic!();
}
Ok(())
}
#[test]
fn test_ipv4_address_parsing() -> Result<(), ParseTestError> {
let address = Host::parse("1.0.0.1")?;
if let Host::Ipv4(ip) = address {
assert_eq!(ip, "1.0.0.1".parse::<Ipv4Addr>()?);
} else {
panic!();
}
Ok(())
}
#[test]
fn test_ipv6_address_parsing() -> Result<(), ParseTestError> {
let address = Host::parse("[::1]")?;
if let Host::Ipv6(ip) = address {
assert_eq!(ip, "::1".parse::<Ipv6Addr>()?);
} else {
panic!();
}
Ok(())
}
}

View File

@ -1,3 +1,5 @@
extern crate url;
use address::{Address, ExplicitlyTypedAddress};
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::num::Wrapping;
@ -35,6 +37,7 @@ pub struct StatefulSdpAnonymizer {
ips: HashMap<IpAddr, IpAddr>,
ip_v4_inc: Wrapping<u32>,
ip_v6_inc: Wrapping<u128>,
host_names: AnonymizationStrMap,
ports: HashMap<u32, u32>,
port_inc: Wrapping<u32>,
origin_users: AnonymizationStrMap,
@ -56,6 +59,7 @@ impl StatefulSdpAnonymizer {
ips: HashMap::new(),
ip_v4_inc: Wrapping(0),
ip_v6_inc: Wrapping(0),
host_names: AnonymizationStrMap::new("fqdn-", 8),
ports: HashMap::new(),
port_inc: Wrapping(0),
origin_users: AnonymizationStrMap::new("origin-user-", 8),
@ -66,6 +70,10 @@ impl StatefulSdpAnonymizer {
}
}
pub fn mask_host(&mut self, host: &str) -> String {
self.host_names.mask(host)
}
pub fn mask_ip(&mut self, addr: &IpAddr) -> IpAddr {
if let Some(address) = self.ips.get(addr) {
return *address;
@ -84,6 +92,29 @@ impl StatefulSdpAnonymizer {
mapped
}
pub fn mask_address(&mut self, address: &Address) -> Address {
match address {
Address::Fqdn(host) => Address::Fqdn(self.mask_host(host)),
Address::Ip(ip) => Address::Ip(self.mask_ip(ip)),
}
}
pub fn mask_typed_address(
&mut self,
address: &ExplicitlyTypedAddress,
) -> ExplicitlyTypedAddress {
match address {
ExplicitlyTypedAddress::Fqdn {
address_type,
domain,
} => ExplicitlyTypedAddress::Fqdn {
address_type: *address_type,
domain: self.mask_host(domain),
},
ExplicitlyTypedAddress::Ip(ip) => ExplicitlyTypedAddress::Ip(self.mask_ip(ip)),
}
}
pub fn mask_port(&mut self, port: u32) -> u32 {
if let Some(stored) = self.ports.get(&port) {
return *stored;

View File

@ -1,11 +1,14 @@
extern crate url;
use std::convert::TryFrom;
use std::fmt;
use std::iter;
use std::net::IpAddr;
use std::str::FromStr;
use error::SdpParserInternalError;
use network::{address_to_string, parse_address_type, parse_network_type, parse_unicast_address};
use network::{parse_network_type, parse_unicast_address};
use SdpType;
use address::{Address, AddressType, ExplicitlyTypedAddress};
use anonymizer::{AnonymizingClone, StatefulSdpAnonymizer};
// Serialization helper marcos and functions
@ -164,10 +167,10 @@ pub struct SdpAttributeCandidate {
pub component: u32,
pub transport: SdpAttributeCandidateTransport,
pub priority: u64,
pub address: IpAddr,
pub address: Address,
pub port: u32,
pub c_type: SdpAttributeCandidateType,
pub raddr: Option<IpAddr>,
pub raddr: Option<Address>,
pub rport: Option<u32>,
pub tcp_type: Option<SdpAttributeCandidateTcpType>,
pub generation: Option<u32>,
@ -182,7 +185,7 @@ impl SdpAttributeCandidate {
component: u32,
transport: SdpAttributeCandidateTransport,
priority: u64,
address: IpAddr,
address: Address,
port: u32,
c_type: SdpAttributeCandidateType,
) -> SdpAttributeCandidate {
@ -204,8 +207,8 @@ impl SdpAttributeCandidate {
}
}
fn set_remote_address(&mut self, ip: IpAddr) {
self.raddr = Some(ip)
fn set_remote_address(&mut self, addr: Address) {
self.raddr = Some(addr)
}
fn set_remote_port(&mut self, p: u32) {
@ -265,9 +268,12 @@ impl ToString for SdpAttributeCandidate {
impl AnonymizingClone for SdpAttributeCandidate {
fn masked_clone(&self, anonymizer: &mut StatefulSdpAnonymizer) -> Self {
let mut masked = self.clone();
masked.address = anonymizer.mask_ip(&self.address);
masked.address = anonymizer.mask_address(&self.address);
masked.port = anonymizer.mask_port(self.port);
masked.raddr = self.raddr.and_then(|addr| Some(anonymizer.mask_ip(&addr)));
masked.raddr = self
.raddr
.clone()
.and_then(|addr| Some(anonymizer.mask_address(&addr)));
masked.rport = self.rport.and_then(|port| Some(anonymizer.mask_port(port)));
masked
}
@ -293,7 +299,7 @@ impl ToString for SdpAttributeDtlsMessage {
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct SdpAttributeRemoteCandidate {
pub component: u32,
pub address: IpAddr,
pub address: Address,
pub port: u32,
}
@ -302,7 +308,7 @@ impl ToString for SdpAttributeRemoteCandidate {
format!(
"{component} {addr} {port}",
component = self.component.to_string(),
addr = self.address.to_string(),
addr = self.address,
port = self.port.to_string()
)
}
@ -311,7 +317,7 @@ impl ToString for SdpAttributeRemoteCandidate {
impl AnonymizingClone for SdpAttributeRemoteCandidate {
fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
SdpAttributeRemoteCandidate {
address: anon.mask_ip(&self.address),
address: anon.mask_address(&self.address),
port: anon.mask_port(self.port),
component: self.component,
}
@ -401,7 +407,7 @@ impl ToString for SdpAttributeSimulcast {
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct SdpAttributeRtcp {
pub port: u16,
pub unicast_addr: Option<IpAddr>,
pub unicast_addr: Option<ExplicitlyTypedAddress>,
}
impl SdpAttributeRtcp {
@ -412,22 +418,17 @@ impl SdpAttributeRtcp {
}
}
fn set_addr(&mut self, addr: IpAddr) {
fn set_addr(&mut self, addr: ExplicitlyTypedAddress) {
self.unicast_addr = Some(addr)
}
}
impl ToString for SdpAttributeRtcp {
fn to_string(&self) -> String {
let unicast_addr_str_opt = match self.unicast_addr {
None => None,
Some(x) => Some(address_to_string(x)),
};
format!(
"{port}{unicast_addr}",
port = self.port.to_string(),
unicast_addr = option_to_string!(" {}", unicast_addr_str_opt)
)
impl fmt::Display for SdpAttributeRtcp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.unicast_addr {
Some(ref addr) => write!(f, "{} {}", self.port, addr),
None => self.port.fmt(f),
}
}
}
@ -666,7 +667,7 @@ impl AnonymizingClone for SdpAttributeFingerprint {
}
}
fn imageattr_discrete_value_list_to_string<T>(values: Vec<T>) -> String
fn imageattr_discrete_value_list_to_string<T>(values: &[T]) -> String
where
T: ToString,
{
@ -700,7 +701,7 @@ impl ToString for SdpAttributeImageAttrXYRange {
}
}
SdpAttributeImageAttrXYRange::DiscreteValues(ref values) => {
imageattr_discrete_value_list_to_string(values.to_vec())
imageattr_discrete_value_list_to_string(values)
}
}
}
@ -718,7 +719,7 @@ impl ToString for SdpAttributeImageAttrSRange {
match *self {
SdpAttributeImageAttrSRange::Range(ref min, ref max) => format!("[{}-{}]", min, max),
SdpAttributeImageAttrSRange::DiscreteValues(ref values) => {
imageattr_discrete_value_list_to_string(values.to_vec())
imageattr_discrete_value_list_to_string(values)
}
}
}
@ -1518,7 +1519,7 @@ fn parse_candidate(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalErro
}
};
let priority = tokens[3].parse::<u64>()?;
let address = parse_unicast_address(tokens[4])?;
let address = Address::from_str(tokens[4])?;
let port = tokens[5].parse::<u32>()?;
if port > 65535 {
return Err(SdpParserInternalError::Generic(
@ -1999,6 +2000,10 @@ fn parse_imageattr_tokens(to_parse: &str, separator: char) -> Vec<String> {
}
fn parse_imagettr_braced_token(to_parse: &str) -> Option<&str> {
if !to_parse.starts_with('[') {
return None;
}
if !to_parse.ends_with(']') {
return None;
}
@ -2492,24 +2497,17 @@ fn parse_rtcp(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
));
}
Some(x) => {
let addrtype = parse_address_type(x)?;
let addrtype = AddressType::from_str(x)?;
let addr = match tokens.next() {
None => {
return Err(SdpParserInternalError::Generic(
"Rtcp attribute is missing ip address token".to_string(),
));
}
Some(x) => {
let addr = parse_unicast_address(x)?;
if !addrtype.same_protocol(&addr) {
return Err(SdpParserInternalError::Generic(
"Failed to parse unicast address attribute.\
addrtype does not match address."
.to_string(),
));
}
addr
}
Some(x) => match ExplicitlyTypedAddress::try_from((addrtype, x)) {
Ok(address) => address,
Err(e) => return Err(e),
},
};
rtcp.set_addr(addr);
}
@ -2776,7 +2774,9 @@ pub fn parse_attribute(value: &str) -> Result<SdpType, SdpParserInternalError> {
#[cfg(test)]
mod tests {
extern crate url;
use super::*;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
macro_rules! make_check_parse {
($attr_type:ty, $attr_kind:path) => {
@ -2867,13 +2867,13 @@ mod tests {
assert_eq!(candidate.priority, 1_685_987_071);
assert_eq!(
candidate.address,
IpAddr::from_str("24.23.204.141").unwrap()
Address::from_str("24.23.204.141").unwrap()
);
assert_eq!(candidate.port, 54609);
assert_eq!(candidate.c_type, SdpAttributeCandidateType::Srflx);
assert_eq!(
candidate.raddr,
Some(IpAddr::from_str("192.168.1.4").unwrap())
Some(Address::from_str("192.168.1.4").unwrap())
);
assert_eq!(candidate.rport, Some(61665));
assert_eq!(
@ -2890,16 +2890,15 @@ mod tests {
}
#[test]
fn test_anonymize_attribute_candidate() {
fn test_anonymize_attribute_candidate() -> Result<(), SdpParserInternalError> {
let mut anon = StatefulSdpAnonymizer::new();
let candidate_1 =
parse_attribute("candidate:0 1 TCP 2122252543 ::8 49760 typ host").unwrap();
let candidate_1 = parse_attribute("candidate:0 1 TCP 2122252543 ::8 49760 typ host")?;
let candidate_2 =
parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 19361 typ srflx").unwrap();
let candidate_3 = parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd").unwrap();
parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 19361 typ srflx")?;
let candidate_3 = parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd")?;
if let SdpType::Attribute(SdpAttribute::Candidate(candidate)) = candidate_1 {
let masked = candidate.masked_clone(&mut anon);
assert!(masked.address == std::net::Ipv6Addr::from(1));
assert!(masked.address == Address::Ip(IpAddr::V6(Ipv6Addr::from(1))));
assert!(masked.port == 1);
} else {
unreachable!();
@ -2907,7 +2906,7 @@ mod tests {
if let SdpType::Attribute(SdpAttribute::Candidate(candidate)) = candidate_2 {
let masked = candidate.masked_clone(&mut anon);
assert!(masked.address == std::net::Ipv4Addr::from(1));
assert!(masked.address == Address::Ip(IpAddr::V4(Ipv4Addr::from(1))));
assert!(masked.port == 2);
} else {
unreachable!();
@ -2915,13 +2914,14 @@ mod tests {
if let SdpType::Attribute(SdpAttribute::Candidate(candidate)) = candidate_3 {
let masked = candidate.masked_clone(&mut anon);
assert!(masked.address == std::net::Ipv4Addr::from(2));
assert!(masked.address == Address::Ip(IpAddr::V4(Ipv4Addr::from(2))));
assert!(masked.port == 3);
assert!(masked.raddr.unwrap() == std::net::Ipv4Addr::from(3));
assert!(masked.raddr.unwrap() == Address::Ip(IpAddr::V4(Ipv4Addr::from(3))));
assert!(masked.rport.unwrap() == 4);
} else {
unreachable!();
}
Ok(())
}
#[test]
@ -2935,7 +2935,7 @@ mod tests {
parse_attribute("candidate:0 1 FOO 2122252543 172.16.156.106 49760 typ host").is_err()
);
assert!(parse_attribute("candidate:0 1 UDP foo 172.16.156.106 49760 typ host").is_err());
assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156 49760 typ host").is_err());
assert!(parse_attribute("candidate:0 1 UDP 2122252543 372.16.356 49760 typ host").is_err());
assert!(
parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 70000 typ host").is_err()
);
@ -2959,17 +2959,13 @@ mod tests {
)
.is_err());
assert!(parse_attribute(
"candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1 rport 61665"
"candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 1%92.168.1 rport 61665"
)
.is_err());
assert!(parse_attribute(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype foobar"
)
.is_err());
assert!(parse_attribute(
"candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1 rport 61665"
)
.is_err());
assert!(parse_attribute(
"candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 70000"
)
@ -2987,8 +2983,7 @@ mod tests {
check_parse_and_serialize("dtls-message:client IGlzdCBl/W4gUeiBtaXQg+JSB1bmQCAkJJkSNEQ=");
check_parse_and_serialize("dtls-message:server IGlzdCBl/W4gUeiBtaXQg+JSB1bmQCAkJJkSNEQ=");
let mut dtls_message = check_parse("dtls-message:client SGVsbG8gV29ybGQ=");
match dtls_message {
match check_parse("dtls-message:client SGVsbG8gV29ybGQ=") {
SdpAttributeDtlsMessage::Client(x) => {
assert_eq!(x, "SGVsbG8gV29ybGQ=");
}
@ -2997,8 +2992,7 @@ mod tests {
}
}
dtls_message = check_parse("dtls-message:server SGVsbG8gV29ybGQ=");
match dtls_message {
match check_parse("dtls-message:server SGVsbG8gV29ybGQ=") {
SdpAttributeDtlsMessage::Server(x) => {
assert_eq!(x, "SGVsbG8gV29ybGQ=");
}
@ -3153,17 +3147,16 @@ mod tests {
}
#[test]
fn test_anonymize_attribute_fingerprint() {
fn test_anonymize_attribute_fingerprint() -> Result<(), SdpParserInternalError> {
let mut anon = StatefulSdpAnonymizer::new();
let print_1 = parse_attribute(
if let SdpType::Attribute(SdpAttribute::Fingerprint(print)) = parse_attribute(
"fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC",
)
.unwrap();
if let SdpType::Attribute(SdpAttribute::Fingerprint(print)) = print_1 {
)? {
assert!(print.masked_clone(&mut anon).to_string() == "sha-1 00:00:00:00:00:00:00:01");
} else {
unreachable!();
}
Ok(())
}
#[test]
@ -3286,6 +3279,10 @@ mod tests {
assert!(parse_attribute("imageattr:").is_err());
assert!(parse_attribute("imageattr:100").is_err());
assert!(parse_attribute("imageattr:120 send * recv * send *").is_err());
assert!(parse_attribute("imageattr:99 send [x=320]").is_err());
assert!(parse_attribute("imageattr:99 recv [y=240]").is_err());
assert!(parse_attribute("imageattr:99 send [x=320,y=240").is_err());
assert!(parse_attribute("imageattr:99 send x=320,y=240]").is_err());
assert!(
parse_attribute("imageattr:97 send [x=800,y=640,sar=1.1] send [x=330,y=250]").is_err()
);
@ -3484,16 +3481,18 @@ mod tests {
}
#[test]
fn test_anonymize_remote_candidate() {
fn test_anonymize_remote_candidate() -> Result<(), SdpParserInternalError> {
let mut anon = StatefulSdpAnonymizer::new();
let remote_1 = parse_attribute("remote-candidates:0 10.0.0.1 5555").unwrap();
if let SdpType::Attribute(SdpAttribute::RemoteCandidate(remote)) = remote_1 {
let mut masked = remote.masked_clone(&mut anon);
assert_eq!(masked.address, std::net::Ipv4Addr::from(1));
if let SdpType::Attribute(SdpAttribute::RemoteCandidate(remote)) =
parse_attribute("remote-candidates:0 10.0.0.1 5555")?
{
let masked = remote.masked_clone(&mut anon);
assert_eq!(masked.address, Address::Ip(IpAddr::V4(Ipv4Addr::from(1))));
assert_eq!(masked.port, 1);
} else {
unreachable!();
}
Ok(())
}
#[test]
@ -3581,7 +3580,6 @@ mod tests {
check_parse_and_serialize("remote-candidates:12345 ::1 5555");
assert!(parse_attribute("remote-candidates:abc 10.0.0.1 5555").is_err());
assert!(parse_attribute("remote-candidates:0 10.a.0.1 5555").is_err());
assert!(parse_attribute("remote-candidates:0 10.0.0.1 70000").is_err());
assert!(parse_attribute("remote-candidates:0 10.0.0.1").is_err());
assert!(parse_attribute("remote-candidates:0").is_err());

View File

@ -3,57 +3,68 @@ use serde::ser::{Serialize, SerializeStruct, Serializer};
use std::error;
use std::error::Error;
use std::fmt;
use std::net::AddrParseError;
extern crate url;
use address::AddressType;
use std::num::ParseFloatError;
use std::num::ParseIntError;
#[derive(Debug, Clone)]
pub enum SdpParserInternalError {
UnknownAddressType(String),
AddressTypeMismatch {
found: AddressType,
expected: AddressType,
},
Generic(String),
Unsupported(String),
Integer(ParseIntError),
Float(ParseFloatError),
Address(AddrParseError),
Domain(url::ParseError),
IpAddress(std::net::AddrParseError),
}
const INTERNAL_ERROR_MESSAGE_UNKNOWN_ADDRESS_TYPE: &str = "Unknown address type";
const INTERNAL_ERROR_MESSAGE_ADDRESS_TYPE_MISMATCH: &str =
"Address is of a different type(1) than declared(2)";
impl fmt::Display for SdpParserInternalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SdpParserInternalError::Generic(ref message) => {
write!(f, "Generic parsing error: {}", message)
}
SdpParserInternalError::UnknownAddressType(ref unknown) => write!(
f,
"{}: {}",
INTERNAL_ERROR_MESSAGE_UNKNOWN_ADDRESS_TYPE, unknown
),
SdpParserInternalError::AddressTypeMismatch { found, expected } => write!(
f,
"{}: {}, {}",
INTERNAL_ERROR_MESSAGE_ADDRESS_TYPE_MISMATCH, found, expected
),
SdpParserInternalError::Generic(ref message) => write!(f, "Parsing error: {}", message),
SdpParserInternalError::Unsupported(ref message) => {
write!(f, "Unsupported parsing error: {}", message)
}
SdpParserInternalError::Integer(ref error) => {
write!(f, "Integer parsing error: {}", error.description())
write!(f, "Integer parsing error: {}", error)
}
SdpParserInternalError::Float(ref error) => {
write!(f, "Float parsing error: {}", error.description())
SdpParserInternalError::Float(ref error) => write!(f, "Float parsing error: {}", error),
SdpParserInternalError::Domain(ref error) => {
write!(f, "Domain name parsing error: {}", error)
}
SdpParserInternalError::Address(ref error) => {
write!(f, "IP address parsing error: {}", error.description())
SdpParserInternalError::IpAddress(ref error) => {
write!(f, "IP address parsing error: {}", error)
}
}
}
}
impl error::Error for SdpParserInternalError {
fn description(&self) -> &str {
match *self {
SdpParserInternalError::Generic(ref message)
| SdpParserInternalError::Unsupported(ref message) => message,
SdpParserInternalError::Integer(ref error) => error.description(),
SdpParserInternalError::Float(ref error) => error.description(),
SdpParserInternalError::Address(ref error) => error.description(),
}
}
impl Error for SdpParserInternalError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
SdpParserInternalError::Integer(ref error) => Some(error),
SdpParserInternalError::Float(ref error) => Some(error),
SdpParserInternalError::Address(ref error) => Some(error),
SdpParserInternalError::Domain(ref error) => Some(error),
SdpParserInternalError::IpAddress(ref error) => Some(error),
// Can't tell much more about our internal errors
_ => None,
}
@ -137,9 +148,7 @@ impl fmt::Display for SdpParserError {
} => write!(
f,
"Line error: {} in line({}): {}",
error.description(),
line_number,
line
error, line_number, line
),
SdpParserError::Unsupported {
ref error,
@ -148,9 +157,7 @@ impl fmt::Display for SdpParserError {
} => write!(
f,
"Unsupported: {} in line({}): {}",
error.description(),
line_number,
line
error, line_number, line
),
SdpParserError::Sequence {
ref message,
@ -160,15 +167,7 @@ impl fmt::Display for SdpParserError {
}
}
impl error::Error for SdpParserError {
fn description(&self) -> &str {
match *self {
SdpParserError::Line { ref error, .. }
| SdpParserError::Unsupported { ref error, .. } => error.description(),
SdpParserError::Sequence { ref message, .. } => message,
}
}
impl Error for SdpParserError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
SdpParserError::Line { ref error, .. }
@ -185,9 +184,15 @@ impl From<ParseIntError> for SdpParserInternalError {
}
}
impl From<AddrParseError> for SdpParserInternalError {
fn from(err: AddrParseError) -> SdpParserInternalError {
SdpParserInternalError::Address(err)
impl From<url::ParseError> for SdpParserInternalError {
fn from(err: url::ParseError) -> SdpParserInternalError {
SdpParserInternalError::Domain(err)
}
}
impl From<std::net::AddrParseError> for SdpParserInternalError {
fn from(err: std::net::AddrParseError) -> SdpParserInternalError {
SdpParserInternalError::IpAddress(err)
}
}
@ -200,15 +205,39 @@ impl From<ParseFloatError> for SdpParserInternalError {
#[cfg(test)]
mod tests {
use super::*;
use address::Address;
use std::str::FromStr;
#[test]
fn test_sdp_parser_internal_error_unknown_address_type() {
let error = SdpParserInternalError::UnknownAddressType("foo".to_string());
assert_eq!(
format!("{}", error),
format!("{}: {}", INTERNAL_ERROR_MESSAGE_UNKNOWN_ADDRESS_TYPE, "foo")
);
assert!(error.source().is_none());
}
#[test]
fn test_sdp_parser_internal_error_address_type_mismatch() {
let error = SdpParserInternalError::AddressTypeMismatch {
found: AddressType::IpV4,
expected: AddressType::IpV6,
};
assert_eq!(
format!("{}", error),
format!(
"{}: {}, {}",
INTERNAL_ERROR_MESSAGE_ADDRESS_TYPE_MISMATCH,
AddressType::IpV4,
AddressType::IpV6
)
);
assert!(error.source().is_none());
}
#[test]
fn test_sdp_parser_internal_error_generic() {
let generic = SdpParserInternalError::Generic("generic message".to_string());
assert_eq!(
format!("{}", generic),
"Generic parsing error: generic message"
);
assert_eq!(generic.description(), "generic message");
assert_eq!(format!("{}", generic), "Parsing error: generic message");
assert!(generic.source().is_none());
}
@ -220,7 +249,6 @@ mod tests {
format!("{}", unsupported),
"Unsupported parsing error: unsupported internal message"
);
assert_eq!(unsupported.description(), "unsupported internal message");
assert!(unsupported.source().is_none());
}
@ -234,7 +262,6 @@ mod tests {
format!("{}", int_err),
"Integer parsing error: invalid digit found in string"
);
assert_eq!(int_err.description(), "invalid digit found in string");
assert!(!int_err.source().is_none());
}
@ -248,23 +275,17 @@ mod tests {
format!("{}", int_err),
"Float parsing error: invalid float literal"
);
assert_eq!(int_err.description(), "invalid float literal");
assert!(!int_err.source().is_none());
}
#[test]
fn test_sdp_parser_internal_error_address() {
let v = "127.0.0.a";
use std::net::IpAddr;
use std::str::FromStr;
let addr = IpAddr::from_str(v);
assert!(addr.is_err());
let addr_err = SdpParserInternalError::Address(addr.err().unwrap());
let v = "127.0.0.500";
let addr_err = Address::from_str(v).err().unwrap();
assert_eq!(
format!("{}", addr_err),
"IP address parsing error: invalid IP address syntax"
"Domain name parsing error: invalid IPv4 address"
);
assert_eq!(addr_err.description(), "invalid IP address syntax");
assert!(!addr_err.source().is_none());
}
@ -277,9 +298,8 @@ mod tests {
};
assert_eq!(
format!("{}", line1),
"Line error: test message in line(13): test line"
"Line error: Parsing error: test message in line(13): test line"
);
assert_eq!(line1.description(), "test message");
assert!(line1.source().is_some());
}
@ -292,9 +312,8 @@ mod tests {
};
assert_eq!(
format!("{}", unsupported1),
"Unsupported: unsupported value in line(21): unsupported line"
"Unsupported: Parsing error: unsupported value in line(21): unsupported line"
);
assert_eq!(unsupported1.description(), "unsupported value");
assert!(unsupported1.source().is_some());
}
@ -308,7 +327,6 @@ mod tests {
format!("{}", sequence1),
"Sequence error in line(42): sequence message"
);
assert_eq!(sequence1.description(), "sequence message");
assert!(sequence1.source().is_none());
}
}

View File

@ -1,23 +1,26 @@
#![cfg_attr(feature = "clippy", feature(plugin))]
#![warn(clippy::all)]
#![forbid(unsafe_code)]
#[macro_use]
extern crate log;
#[cfg(feature = "serialize")]
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
extern crate enum_display_derive;
#[cfg(feature = "serialize")]
extern crate serde;
use std::net::IpAddr;
use std::str::FromStr;
use std::convert::TryFrom;
#[macro_use]
pub mod attribute_type;
pub mod address;
pub mod anonymizer;
pub mod error;
pub mod media_type;
pub mod network;
use address::{AddressTyped, ExplicitlyTypedAddress};
use anonymizer::{AnonymizingClone, StatefulSdpAnonymizer};
use attribute_type::{
parse_attribute, SdpAttribute, SdpAttributeRid, SdpAttributeSimulcastVersion, SdpAttributeType,
@ -28,7 +31,7 @@ use media_type::{
parse_media, parse_media_vector, SdpFormatList, SdpMedia, SdpMediaLine, SdpMediaValue,
SdpProtocolValue,
};
use network::{address_to_string, parse_address_type, parse_network_type, parse_unicast_address};
use network::{parse_address_type, parse_network_type};
#[derive(Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
@ -53,7 +56,7 @@ impl ToString for SdpBandwidth {
#[derive(Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct SdpConnection {
pub address: IpAddr,
pub address: ExplicitlyTypedAddress,
pub ttl: Option<u8>,
pub amount: Option<u32>,
}
@ -62,7 +65,7 @@ impl ToString for SdpConnection {
fn to_string(&self) -> String {
format!(
"{address}{ttl}{amount}",
address = address_to_string(self.address),
address = self.address,
ttl = option_to_string!("/{}", self.ttl),
amount = option_to_string!("/{}", self.amount)
)
@ -72,7 +75,7 @@ impl ToString for SdpConnection {
impl AnonymizingClone for SdpConnection {
fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
let mut masked = self.clone();
masked.address = anon.mask_ip(&self.address);
masked.address = anon.mask_typed_address(&self.address);
masked
}
}
@ -83,7 +86,7 @@ pub struct SdpOrigin {
pub username: String,
pub session_id: u64,
pub session_version: u64,
pub unicast_addr: IpAddr,
pub unicast_addr: ExplicitlyTypedAddress,
}
impl ToString for SdpOrigin {
@ -93,7 +96,7 @@ impl ToString for SdpOrigin {
username = self.username.clone(),
sess_id = self.session_id.to_string(),
sess_vers = self.session_version.to_string(),
unicast_addr = address_to_string(self.unicast_addr)
unicast_addr = self.unicast_addr
)
}
}
@ -102,7 +105,7 @@ impl AnonymizingClone for SdpOrigin {
fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
let mut masked = self.clone();
masked.username = anon.mask_origin_user(&self.username);
masked.unicast_addr = anon.mask_ip(&masked.unicast_addr);
masked.unicast_addr = anon.mask_typed_address(&masked.unicast_addr);
masked
}
}
@ -265,7 +268,7 @@ impl SdpSession {
direction: SdpAttribute,
port: u32,
protocol: SdpProtocolValue,
addr: String,
addr: ExplicitlyTypedAddress,
) -> Result<(), SdpParserInternalError> {
let mut media = SdpMedia::new(SdpMediaLine {
media: media_type,
@ -278,7 +281,7 @@ impl SdpSession {
media.add_attribute(direction)?;
media.set_connection(SdpConnection {
address: IpAddr::from_str(addr.as_str())?,
address: addr,
ttl: None,
amount: None,
})?;
@ -401,9 +404,9 @@ fn parse_origin(value: &str) -> Result<SdpType, SdpParserInternalError> {
"Origin type is missing IP address token".to_string(),
));
}
Some(x) => parse_unicast_address(x)?,
Some(x) => ExplicitlyTypedAddress::try_from((addrtype, x))?,
};
if !addrtype.same_protocol(&unicast_addr) {
if addrtype != unicast_addr.address_type() {
return Err(SdpParserInternalError::Generic(
"Origin addrtype does not match address.".to_string(),
));
@ -438,12 +441,7 @@ fn parse_connection(value: &str) -> Result<SdpType, SdpParserInternalError> {
ttl = Some(addr_tokens[1].parse::<u8>()?);
addr_token = addr_tokens[0];
}
let address = parse_unicast_address(addr_token)?;
if !addrtype.same_protocol(&address) {
return Err(SdpParserInternalError::Generic(
"connection addrtype does not match address.".to_string(),
));
}
let address = ExplicitlyTypedAddress::try_from((addrtype, addr_token))?;
let c = SdpConnection {
address,
ttl,
@ -587,10 +585,13 @@ fn parse_sdp_line(line: &str, line_number: usize) -> Result<SdpLine, SdpParserEr
sdp_type,
})
.map_err(|e| match e {
SdpParserInternalError::Generic(..)
SdpParserInternalError::UnknownAddressType(..)
| SdpParserInternalError::AddressTypeMismatch { .. }
| SdpParserInternalError::Generic(..)
| SdpParserInternalError::Integer(..)
| SdpParserInternalError::Float(..)
| SdpParserInternalError::Address(..) => SdpParserError::Line {
| SdpParserInternalError::Domain(..)
| SdpParserInternalError::IpAddress(..) => SdpParserError::Line {
error: e,
line: line.to_string(),
line_number,
@ -734,7 +735,6 @@ fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError>
Ok(())
}
// TODO add unit tests
fn parse_sdp_vector(lines: &mut Vec<SdpLine>) -> Result<SdpSession, SdpParserError> {
if lines.len() < 4 {
return Err(SdpParserError::Sequence {
@ -875,9 +875,13 @@ pub fn parse_sdp(sdp: &str, fail_on_warning: bool) -> Result<SdpSession, SdpPars
#[cfg(test)]
mod tests {
extern crate url;
use super::*;
use address::{Address, AddressType};
use anonymizer::ToBytesVec;
use media_type::create_dummy_media_section;
use std::net::IpAddr;
use std::net::Ipv4Addr;
fn create_dummy_sdp_session() -> SdpSession {
let origin = parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0");
@ -900,13 +904,15 @@ mod tests {
}
#[test]
fn test_session_works() {
assert!(parse_session("topic").is_ok());
fn test_session_works() -> Result<(), SdpParserInternalError> {
parse_session("topic")?;
Ok(())
}
#[test]
fn test_version_works() {
assert!(parse_version("0").is_ok());
fn test_version_works() -> Result<(), SdpParserInternalError> {
parse_version("0")?;
Ok(())
}
#[test]
@ -917,9 +923,10 @@ mod tests {
}
#[test]
fn test_origin_works() {
assert!(parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0").is_ok());
assert!(parse_origin("mozilla 506705521068071134 0 IN IP6 2001:db8::1").is_ok());
fn test_origin_works() -> Result<(), SdpParserInternalError> {
parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0")?;
parse_origin("mozilla 506705521068071134 0 IN IP6 2001:db8::1")?;
Ok(())
}
#[test]
@ -974,16 +981,18 @@ mod tests {
}
#[test]
fn connection_works() {
assert!(parse_connection("IN IP4 127.0.0.1").is_ok());
assert!(parse_connection("IN IP4 127.0.0.1/10/10").is_ok());
assert!(parse_connection("IN IP6 ::1").is_ok());
assert!(parse_connection("IN IP6 ::1/1/1").is_ok());
fn connection_works() -> Result<(), SdpParserInternalError> {
parse_connection("IN IP4 127.0.0.1")?;
parse_connection("IN IP4 127.0.0.1/10/10")?;
parse_connection("IN IP6 ::1")?;
parse_connection("IN IP6 ::1/1/1")?;
Ok(())
}
#[test]
fn connection_lots_of_whitespace() {
assert!(parse_connection("IN IP4 127.0.0.1").is_ok());
fn connection_lots_of_whitespace() -> Result<(), SdpParserInternalError> {
parse_connection("IN IP4 127.0.0.1")?;
Ok(())
}
#[test]
@ -1014,10 +1023,11 @@ mod tests {
}
#[test]
fn bandwidth_works() {
assert!(parse_bandwidth("AS:1").is_ok());
assert!(parse_bandwidth("CT:123").is_ok());
assert!(parse_bandwidth("TIAS:12345").is_ok());
fn bandwidth_works() -> Result<(), SdpParserInternalError> {
parse_bandwidth("AS:1")?;
parse_bandwidth("CT:123")?;
parse_bandwidth("TIAS:12345")?;
Ok(())
}
#[test]
@ -1027,13 +1037,15 @@ mod tests {
}
#[test]
fn bandwidth_unsupported_type() {
assert!(parse_bandwidth("UNSUPPORTED:12345").is_ok());
fn bandwidth_unsupported_type() -> Result<(), SdpParserInternalError> {
parse_bandwidth("UNSUPPORTED:12345")?;
Ok(())
}
#[test]
fn test_timing_works() {
assert!(parse_timing("0 0").is_ok());
fn test_timing_works() -> Result<(), SdpParserInternalError> {
parse_timing("0 0")?;
Ok(())
}
#[test]
@ -1049,9 +1061,10 @@ mod tests {
}
#[test]
fn test_parse_sdp_line_works() {
assert!(parse_sdp_line("v=0", 0).is_ok());
assert!(parse_sdp_line("s=somesession", 0).is_ok());
fn test_parse_sdp_line_works() -> Result<(), SdpParserError> {
parse_sdp_line("v=0", 0)?;
parse_sdp_line("s=somesession", 0)?;
Ok(())
}
#[test]
@ -1099,8 +1112,9 @@ mod tests {
}
#[test]
fn test_parse_sdp_line_valid_a_line() {
assert!(parse_sdp_line("a=rtpmap:8 PCMA/8000", 0).is_ok());
fn test_parse_sdp_line_valid_a_line() -> Result<(), SdpParserError> {
parse_sdp_line("a=rtpmap:8 PCMA/8000", 0)?;
Ok(())
}
#[test]
@ -1109,7 +1123,17 @@ mod tests {
}
#[test]
fn test_sanity_check_sdp_session_timing() {
fn test_add_attribute() -> Result<(), SdpParserInternalError> {
let mut sdp_session = create_dummy_sdp_session();
sdp_session.add_attribute(SdpAttribute::Sendrecv)?;
assert!(sdp_session.add_attribute(SdpAttribute::BundleOnly).is_err());
assert_eq!(sdp_session.attribute.len(), 1);
Ok(())
}
#[test]
fn test_sanity_check_sdp_session_timing() -> Result<(), SdpParserError> {
let mut sdp_session = create_dummy_sdp_session();
sdp_session.extend_media(vec![create_dummy_media_section()]);
@ -1118,28 +1142,29 @@ mod tests {
let t = SdpTiming { start: 0, stop: 0 };
sdp_session.set_timing(t);
assert!(sanity_check_sdp_session(&sdp_session).is_ok());
sanity_check_sdp_session(&sdp_session)?;
Ok(())
}
#[test]
fn test_sanity_check_sdp_session_media() {
fn test_sanity_check_sdp_session_media() -> Result<(), SdpParserError> {
let mut sdp_session = create_dummy_sdp_session();
let t = SdpTiming { start: 0, stop: 0 };
sdp_session.set_timing(t);
assert!(sanity_check_sdp_session(&sdp_session).is_ok());
sanity_check_sdp_session(&sdp_session)?;
sdp_session.extend_media(vec![create_dummy_media_section()]);
assert!(sanity_check_sdp_session(&sdp_session).is_ok());
sanity_check_sdp_session(&sdp_session)?;
Ok(())
}
#[test]
fn test_sanity_check_sdp_connection() {
let origin = parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0");
assert!(origin.is_ok());
fn test_sanity_check_sdp_connection() -> Result<(), SdpParserInternalError> {
let origin = parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0")?;
let mut sdp_session;
if let SdpType::Origin(o) = origin.unwrap() {
if let SdpType::Origin(o) = origin {
sdp_session = SdpSession::new(0, o, "-".to_string());
} else {
unreachable!();
@ -1154,9 +1179,8 @@ mod tests {
assert!(sanity_check_sdp_session(&sdp_session).is_err());
let connection = parse_connection("IN IP6 ::1");
assert!(connection.is_ok());
if let Ok(SdpType::Connection(c)) = connection {
let connection = parse_connection("IN IP6 ::1")?;
if let SdpType::Connection(c) = connection {
sdp_session.connection = Some(c);
} else {
unreachable!();
@ -1165,10 +1189,9 @@ mod tests {
assert!(sanity_check_sdp_session(&sdp_session).is_ok());
let mut second_media = create_dummy_media_section();
let mconnection = parse_connection("IN IP4 0.0.0.0");
assert!(mconnection.is_ok());
if let Ok(SdpType::Connection(c)) = mconnection {
assert!(second_media.set_connection(c).is_ok());
let mconnection = parse_connection("IN IP4 0.0.0.0")?;
if let SdpType::Connection(c) = mconnection {
second_media.set_connection(c)?;
} else {
unreachable!();
}
@ -1176,43 +1199,37 @@ mod tests {
assert!(sdp_session.media.len() == 2);
assert!(sanity_check_sdp_session(&sdp_session).is_ok());
Ok(())
}
#[test]
fn test_sanity_check_sdp_session_extmap() {
fn test_sanity_check_sdp_session_extmap() -> Result<(), SdpParserInternalError> {
let mut sdp_session = create_dummy_sdp_session();
let t = SdpTiming { start: 0, stop: 0 };
sdp_session.set_timing(t);
sdp_session.extend_media(vec![create_dummy_media_section()]);
let attribute =
parse_attribute("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time");
assert!(attribute.is_ok());
let extmap;
if let SdpType::Attribute(a) = attribute.unwrap() {
extmap = a;
parse_attribute("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")?;
if let SdpType::Attribute(a) = attribute {
sdp_session.add_attribute(a)?;
} else {
unreachable!();
}
let ret = sdp_session.add_attribute(extmap);
assert!(ret.is_ok());
assert!(sdp_session
.get_attribute(SdpAttributeType::Extmap)
.is_some());
assert!(sanity_check_sdp_session(&sdp_session).is_ok());
let mut second_media = create_dummy_media_section();
let mattribute =
parse_attribute("extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level");
assert!(mattribute.is_ok());
let mextmap;
if let SdpType::Attribute(ma) = mattribute.unwrap() {
mextmap = ma;
parse_attribute("extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level")?;
if let SdpType::Attribute(ma) = mattribute {
second_media.add_attribute(ma)?;
} else {
unreachable!();
}
let mut second_media = create_dummy_media_section();
assert!(second_media.add_attribute(mextmap).is_ok());
assert!(second_media
.get_attribute(SdpAttributeType::Extmap)
.is_some());
@ -1225,16 +1242,18 @@ mod tests {
sdp_session.attribute = Vec::new();
assert!(sanity_check_sdp_session(&sdp_session).is_ok());
Ok(())
}
#[test]
fn test_sanity_check_sdp_session_simulcast() {
fn test_sanity_check_sdp_session_simulcast() -> Result<(), SdpParserError> {
let mut sdp_session = create_dummy_sdp_session();
let t = SdpTiming { start: 0, stop: 0 };
sdp_session.set_timing(t);
sdp_session.extend_media(vec![create_dummy_media_section()]);
assert!(sanity_check_sdp_session(&sdp_session).is_ok());
sanity_check_sdp_session(&sdp_session)?;
Ok(())
}
#[test]
@ -1248,16 +1267,16 @@ mod tests {
}
#[test]
fn test_parse_sdp_minimal_sdp_successfully() {
assert!(parse_sdp(
fn test_parse_sdp_minimal_sdp_successfully() -> Result<(), SdpParserError> {
parse_sdp(
"v=0\r\n
o=- 0 0 IN IP6 ::1\r\n
s=-\r\n
c=IN IP6 ::1\r\n
t=0 0\r\n",
true
)
.is_ok());
true,
)?;
Ok(())
}
#[test]
@ -1298,8 +1317,8 @@ m=foobar 0 UDP/TLS/RTP/SAVPF 0\r\n",
}
#[test]
fn test_parse_sdp_unsupported_warning() {
assert!(parse_sdp(
fn test_parse_sdp_unsupported_warning() -> Result<(), SdpParserError> {
parse_sdp(
"v=0\r\n
o=- 0 0 IN IP4 0.0.0.0\r\n
s=-\r\n
@ -1307,9 +1326,9 @@ c=IN IP4 198.51.100.7\r\n
t=0 0\r\n
m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n
a=unsupported\r\n",
false
)
.is_ok());
false,
)?;
Ok(())
}
#[test]
@ -1317,8 +1336,9 @@ a=unsupported\r\n",
assert!(parse_sdp(
"v=0\r\n
o=- 0 0 IN IP4 0.0.0.0\r\n
t=0 0\r\n
s=-\r\n
t=0 0\r\n
a=bundle-only\r\n
m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n",
true
)
@ -1389,7 +1409,10 @@ a=ice-lite\r\n",
for _ in 0..2 {
let masked = origin_1.masked_clone(&mut anon);
assert_eq!(masked.username, "origin-user-00000001");
assert_eq!(masked.unicast_addr, std::net::Ipv4Addr::from(1));
assert_eq!(
masked.unicast_addr,
ExplicitlyTypedAddress::Ip(IpAddr::V4(Ipv4Addr::from(1)))
);
}
} else {
unreachable!();
@ -1417,10 +1440,13 @@ a=ice-lite\r\n",
.unwrap();
let mut masked = sdp.masked_clone(&mut anon);
assert_eq!(masked.origin.username, "origin-user-00000001");
assert_eq!(masked.origin.unicast_addr, std::net::Ipv4Addr::from(1));
assert_eq!(
masked.origin.unicast_addr,
ExplicitlyTypedAddress::Ip(IpAddr::V4(Ipv4Addr::from(1)))
);
assert_eq!(
masked.connection.unwrap().address,
std::net::Ipv4Addr::from(2)
ExplicitlyTypedAddress::Ip(IpAddr::V4(Ipv4Addr::from(2)))
);
let mut attributes = masked.attribute;
for m in &mut masked.media {
@ -1428,10 +1454,10 @@ a=ice-lite\r\n",
attributes.push(attribute.clone());
}
}
for mut attribute in attributes {
for attribute in attributes {
match attribute {
SdpAttribute::Candidate(c) => {
assert_eq!(c.address, std::net::Ipv4Addr::from(3));
assert_eq!(c.address, Address::Ip(IpAddr::V4(Ipv4Addr::from(3))));
assert_eq!(c.port, 1);
}
SdpAttribute::Fingerprint(f) => {
@ -1444,11 +1470,165 @@ a=ice-lite\r\n",
assert_eq!(u, "ice-user-00000001");
}
SdpAttribute::RemoteCandidate(r) => {
assert_eq!(r.address, std::net::Ipv4Addr::from(4));
assert_eq!(r.address, Address::Ip(IpAddr::V4(Ipv4Addr::from(4))));
assert_eq!(r.port, 2);
}
_ => {}
}
}
}
#[test]
fn test_parse_session_vector() -> Result<(), SdpParserError> {
let mut sdp_session = create_dummy_sdp_session();
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("a=sendrecv", 1)?);
sdp_session.parse_session_vector(&mut lines)?;
assert_eq!(sdp_session.attribute.len(), 1);
Ok(())
}
#[test]
fn test_parse_session_vector_non_session_attribute() -> Result<(), SdpParserError> {
let mut sdp_session = create_dummy_sdp_session();
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("a=bundle-only", 2)?);
assert!(sdp_session.parse_session_vector(&mut lines).is_err());
assert_eq!(sdp_session.attribute.len(), 0);
Ok(())
}
#[test]
fn test_parse_session_vector_version_repeated() -> Result<(), SdpParserError> {
let mut sdp_session = create_dummy_sdp_session();
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("v=0", 3)?);
assert!(sdp_session.parse_session_vector(&mut lines).is_err());
Ok(())
}
#[test]
fn test_parse_session_vector_contains_media_type() -> Result<(), SdpParserError> {
let mut sdp_session = create_dummy_sdp_session();
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("m=audio 0 UDP/TLS/RTP/SAVPF 0", 4)?);
assert!(sdp_session.parse_session_vector(&mut lines).is_err());
Ok(())
}
#[test]
fn test_parse_sdp_vector_no_media_section() -> Result<(), SdpParserError> {
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("v=0", 1)?);
lines.push(parse_sdp_line(
"o=ausername 4294967296 2 IN IP4 127.0.0.1",
1,
)?);
lines.push(parse_sdp_line("s=SIP Call", 1)?);
lines.push(parse_sdp_line("t=0 0", 1)?);
lines.push(parse_sdp_line("c=IN IP6 ::1", 1)?);
assert!(parse_sdp_vector(&mut lines).is_ok());
Ok(())
}
#[test]
fn test_parse_sdp_vector_with_media_section() -> Result<(), SdpParserError> {
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("v=0", 1)?);
lines.push(parse_sdp_line(
"o=ausername 4294967296 2 IN IP4 127.0.0.1",
1,
)?);
lines.push(parse_sdp_line("s=SIP Call", 1)?);
lines.push(parse_sdp_line("t=0 0", 1)?);
lines.push(parse_sdp_line("m=video 56436 RTP/SAVPF 120", 1)?);
lines.push(parse_sdp_line("c=IN IP6 ::1", 1)?);
assert!(parse_sdp_vector(&mut lines).is_ok());
Ok(())
}
#[test]
fn test_parse_sdp_vector_too_short() -> Result<(), SdpParserError> {
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("v=0", 1)?);
assert!(parse_sdp_vector(&mut lines).is_err());
Ok(())
}
#[test]
fn test_parse_sdp_vector_missing_version() -> Result<(), SdpParserError> {
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line(
"o=ausername 4294967296 2 IN IP4 127.0.0.1",
1,
)?);
for _ in 0..3 {
lines.push(parse_sdp_line("a=sendrecv", 1)?);
}
assert!(parse_sdp_vector(&mut lines).is_err());
Ok(())
}
#[test]
fn test_parse_sdp_vector_missing_origin() -> Result<(), SdpParserError> {
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("v=0", 1)?);
for _ in 0..3 {
lines.push(parse_sdp_line("a=sendrecv", 1)?);
}
assert!(parse_sdp_vector(&mut lines).is_err());
Ok(())
}
#[test]
fn test_parse_sdp_vector_missing_session() -> Result<(), SdpParserError> {
let mut lines: Vec<SdpLine> = Vec::new();
lines.push(parse_sdp_line("v=0", 1)?);
lines.push(parse_sdp_line(
"o=ausername 4294967296 2 IN IP4 127.0.0.1",
1,
)?);
for _ in 0..2 {
lines.push(parse_sdp_line("a=sendrecv", 1)?);
}
assert!(parse_sdp_vector(&mut lines).is_err());
Ok(())
}
#[test]
fn test_session_add_media_works() -> Result<(), SdpParserError> {
let mut sdp_session = create_dummy_sdp_session();
assert!(sdp_session
.add_media(
SdpMediaValue::Audio,
SdpAttribute::Sendrecv,
99,
SdpProtocolValue::RtpSavpf,
ExplicitlyTypedAddress::from(Ipv4Addr::new(127, 0, 0, 1))
)
.is_ok());
assert!(sdp_session.get_connection().is_some());
assert_eq!(sdp_session.attribute.len(), 0);
assert_eq!(sdp_session.media.len(), 1);
assert_eq!(sdp_session.media[0].get_attributes().len(), 1);
assert!(sdp_session.media[0]
.get_attribute(SdpAttributeType::Sendrecv)
.is_some());
Ok(())
}
#[test]
fn test_session_add_media_invalid_attribute_fails() -> Result<(), SdpParserInternalError> {
let mut sdp_session = create_dummy_sdp_session();
assert!(sdp_session
.add_media(
SdpMediaValue::Audio,
SdpAttribute::IceLite,
99,
SdpProtocolValue::RtpSavpf,
ExplicitlyTypedAddress::try_from((AddressType::IpV4, "127.0.0.1"))?
)
.is_err());
Ok(())
}
}

View File

@ -486,11 +486,12 @@ pub fn create_dummy_media_section() -> SdpMedia {
#[cfg(test)]
mod tests {
use super::*;
use address::{AddressType, ExplicitlyTypedAddress};
use attribute_type::{
SdpAttributeFmtp, SdpAttributeFmtpParameters, SdpAttributePayloadType, SdpAttributeRtcpFb,
SdpAttributeRtcpFbType,
};
use network::parse_unicast_address;
use std::convert::TryFrom;
// TODO is this useful outside of tests?
impl SdpFormatList {
@ -574,29 +575,24 @@ mod tests {
}
#[test]
fn test_add_codec() {
fn test_add_codec() -> Result<(), SdpParserInternalError> {
let mut msection = create_dummy_media_section();
assert!(msection
.add_codec(SdpAttributeRtpmap::new(96, "foobar".to_string(), 1000))
.is_ok());
msection.add_codec(SdpAttributeRtpmap::new(96, "foobar".to_string(), 1000))?;
assert_eq!(msection.get_formats().len(), 1);
assert!(msection.get_attribute(SdpAttributeType::Rtpmap).is_some());
let mut msection = create_dummy_media_section();
msection.media.formats = SdpFormatList::Strings(Vec::new());
assert!(msection
.add_codec(SdpAttributeRtpmap::new(97, "boofar".to_string(), 1001))
.is_ok());
msection.add_codec(SdpAttributeRtpmap::new(97, "boofar".to_string(), 1001))?;
assert_eq!(msection.get_formats().len(), 1);
assert!(msection.get_attribute(SdpAttributeType::Rtpmap).is_some());
Ok(())
}
#[test]
fn test_remove_codecs() {
fn test_remove_codecs() -> Result<(), SdpParserInternalError> {
let mut msection = create_dummy_media_section();
assert!(msection
.add_codec(SdpAttributeRtpmap::new(96, "foobar".to_string(), 1000))
.is_ok());
msection.add_codec(SdpAttributeRtpmap::new(96, "foobar".to_string(), 1000))?;
assert_eq!(msection.get_formats().len(), 1);
assert!(msection.get_attribute(SdpAttributeType::Rtpmap).is_some());
msection.remove_codecs();
@ -605,9 +601,7 @@ mod tests {
let mut msection = create_dummy_media_section();
msection.media.formats = SdpFormatList::Strings(Vec::new());
assert!(msection
.add_codec(SdpAttributeRtpmap::new(97, "boofar".to_string(), 1001))
.is_ok());
msection.add_codec(SdpAttributeRtpmap::new(97, "boofar".to_string(), 1001))?;
assert_eq!(msection.get_formats().len(), 1);
add_dummy_attributes(&mut msection);
@ -619,14 +613,13 @@ mod tests {
assert!(msection.get_attribute(SdpAttributeType::Fmtp).is_none());
assert!(msection.get_attribute(SdpAttributeType::Sctpmap).is_none());
assert!(msection.get_attribute(SdpAttributeType::SctpPort).is_none());
Ok(())
}
#[test]
fn test_add_datachannel() {
fn test_add_datachannel() -> Result<(), SdpParserInternalError> {
let mut msection = create_dummy_media_section();
assert!(msection
.add_datachannel("foo".to_string(), 5000, 256, 0)
.is_ok());
msection.add_datachannel("foo".to_string(), 5000, 256, 0)?;
assert_eq!(*msection.get_type(), SdpMediaValue::Application);
assert!(msection.get_attribute(SdpAttributeType::SctpPort).is_none());
assert!(msection
@ -642,9 +635,7 @@ mod tests {
}
let mut msection = create_dummy_media_section();
assert!(msection
.add_datachannel("foo".to_string(), 5000, 256, 1234)
.is_ok());
msection.add_datachannel("foo".to_string(), 5000, 256, 1234)?;
assert_eq!(*msection.get_type(), SdpMediaValue::Application);
assert!(msection.get_attribute(SdpAttributeType::SctpPort).is_none());
assert!(msection
@ -662,9 +653,7 @@ mod tests {
let mut msection = create_dummy_media_section();
msection.media.proto = SdpProtocolValue::UdpDtlsSctp;
assert!(msection
.add_datachannel("foo".to_string(), 5000, 256, 5678)
.is_ok());
msection.add_datachannel("foo".to_string(), 5000, 256, 5678)?;
assert_eq!(*msection.get_type(), SdpMediaValue::Application);
assert!(msection.get_attribute(SdpAttributeType::Sctpmap).is_none());
assert!(msection.get_attribute(SdpAttributeType::SctpPort).is_some());
@ -686,69 +675,58 @@ mod tests {
}
_ => unreachable!(),
}
Ok(())
}
#[test]
fn test_parse_media_token() {
let audio = parse_media_token("audio");
assert!(audio.is_ok());
assert_eq!(audio.unwrap(), SdpMediaValue::Audio);
let video = parse_media_token("VIDEO");
assert!(video.is_ok());
assert_eq!(video.unwrap(), SdpMediaValue::Video);
let app = parse_media_token("aPplIcatIOn");
assert!(app.is_ok());
assert_eq!(app.unwrap(), SdpMediaValue::Application);
fn test_parse_media_token() -> Result<(), SdpParserInternalError> {
let audio = parse_media_token("audio")?;
assert_eq!(audio, SdpMediaValue::Audio);
let video = parse_media_token("VIDEO")?;
assert_eq!(video, SdpMediaValue::Video);
let app = parse_media_token("aPplIcatIOn")?;
assert_eq!(app, SdpMediaValue::Application);
assert!(parse_media_token("").is_err());
assert!(parse_media_token("foobar").is_err());
Ok(())
}
#[test]
fn test_parse_protocol_rtp_token() {
let rtps = parse_protocol_token("rtp/avp");
assert!(rtps.is_ok());
assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpAvp);
let rtps = parse_protocol_token("rtp/avpf");
assert!(rtps.is_ok());
assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpAvpf);
let rtps = parse_protocol_token("rtp/savp");
assert!(rtps.is_ok());
assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpSavp);
let rtps = parse_protocol_token("rtp/savpf");
assert!(rtps.is_ok());
assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpSavpf);
let udps = parse_protocol_token("udp/tls/rtp/savp");
assert!(udps.is_ok());
assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavp);
let udps = parse_protocol_token("udp/tls/rtp/savpf");
assert!(udps.is_ok());
assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavpf);
let tcps = parse_protocol_token("TCP/dtls/rtp/savp");
assert!(tcps.is_ok());
assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpDtlsRtpSavp);
let tcps = parse_protocol_token("TCP/dtls/rtp/savpf");
assert!(tcps.is_ok());
assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpDtlsRtpSavpf);
let tcps = parse_protocol_token("TCP/tls/rtp/savpf");
assert!(tcps.is_ok());
assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpTlsRtpSavpf);
fn test_parse_protocol_rtp_token() -> Result<(), SdpParserInternalError> {
let rtps = parse_protocol_token("rtp/avp")?;
assert_eq!(rtps, SdpProtocolValue::RtpAvp);
let rtps = parse_protocol_token("rtp/avpf")?;
assert_eq!(rtps, SdpProtocolValue::RtpAvpf);
let rtps = parse_protocol_token("rtp/savp")?;
assert_eq!(rtps, SdpProtocolValue::RtpSavp);
let rtps = parse_protocol_token("rtp/savpf")?;
assert_eq!(rtps, SdpProtocolValue::RtpSavpf);
let udps = parse_protocol_token("udp/tls/rtp/savp")?;
assert_eq!(udps, SdpProtocolValue::UdpTlsRtpSavp);
let udps = parse_protocol_token("udp/tls/rtp/savpf")?;
assert_eq!(udps, SdpProtocolValue::UdpTlsRtpSavpf);
let tcps = parse_protocol_token("TCP/dtls/rtp/savp")?;
assert_eq!(tcps, SdpProtocolValue::TcpDtlsRtpSavp);
let tcps = parse_protocol_token("TCP/dtls/rtp/savpf")?;
assert_eq!(tcps, SdpProtocolValue::TcpDtlsRtpSavpf);
let tcps = parse_protocol_token("TCP/tls/rtp/savpf")?;
assert_eq!(tcps, SdpProtocolValue::TcpTlsRtpSavpf);
assert!(parse_protocol_token("").is_err());
assert!(parse_protocol_token("foobar").is_err());
Ok(())
}
#[test]
fn test_parse_protocol_sctp_token() {
let dtls = parse_protocol_token("dtLs/ScTP");
assert!(dtls.is_ok());
assert_eq!(dtls.unwrap(), SdpProtocolValue::DtlsSctp);
let usctp = parse_protocol_token("udp/DTLS/sctp");
assert!(usctp.is_ok());
assert_eq!(usctp.unwrap(), SdpProtocolValue::UdpDtlsSctp);
let tsctp = parse_protocol_token("tcp/dtls/SCTP");
assert!(tsctp.is_ok());
assert_eq!(tsctp.unwrap(), SdpProtocolValue::TcpDtlsSctp);
fn test_parse_protocol_sctp_token() -> Result<(), SdpParserInternalError> {
let dtls = parse_protocol_token("dtLs/ScTP")?;
assert_eq!(dtls, SdpProtocolValue::DtlsSctp);
let usctp = parse_protocol_token("udp/DTLS/sctp")?;
assert_eq!(usctp, SdpProtocolValue::UdpDtlsSctp);
let tsctp = parse_protocol_token("tcp/dtls/SCTP")?;
assert_eq!(tsctp, SdpProtocolValue::TcpDtlsSctp);
Ok(())
}
#[test]
@ -819,9 +797,8 @@ mod tests {
sdp_type: SdpType::Media(media_line),
};
sdp_lines.push(media);
let address = parse_unicast_address("127.0.0.1").unwrap();
let c = SdpConnection {
address,
address: ExplicitlyTypedAddress::try_from((AddressType::IpV4, "127.0.0.1")).unwrap(),
ttl: None,
amount: None,
};

View File

@ -1,22 +1,9 @@
use address::{Address, AddressType};
use error::SdpParserInternalError;
use std::net::IpAddr;
use std::str::FromStr;
use error::SdpParserInternalError;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SdpAddressType {
IP4 = 4,
IP6 = 6,
}
impl SdpAddressType {
pub fn same_protocol(self, addr: &IpAddr) -> bool {
(addr.is_ipv6() && self == SdpAddressType::IP6)
|| (addr.is_ipv4() && self == SdpAddressType::IP4)
}
}
pub fn address_to_string(addr: IpAddr) -> String {
pub fn ip_address_to_string(addr: IpAddr) -> String {
match addr {
IpAddr::V4(ipv4) => format!("IN IP4 {}", ipv4.to_string()),
IpAddr::V6(ipv6) => format!("IN IP6 {}", ipv6.to_string()),
@ -26,26 +13,19 @@ pub fn address_to_string(addr: IpAddr) -> String {
pub fn parse_network_type(value: &str) -> Result<(), SdpParserInternalError> {
if value.to_uppercase() != "IN" {
return Err(SdpParserInternalError::Generic(
"nettype needs to be IN".to_string(),
"nettype must be IN".to_string(),
));
};
Ok(())
}
pub fn parse_address_type(value: &str) -> Result<SdpAddressType, SdpParserInternalError> {
Ok(match value.to_uppercase().as_ref() {
"IP4" => SdpAddressType::IP4,
"IP6" => SdpAddressType::IP6,
_ => {
return Err(SdpParserInternalError::Generic(
"address type needs to be IP4 or IP6".to_string(),
));
}
})
pub fn parse_address_type(value: &str) -> Result<AddressType, SdpParserInternalError> {
AddressType::from_str(value.to_uppercase().as_str())
.map_err(|_| SdpParserInternalError::Generic("address type must be IP4 or IP6".to_string()))
}
pub fn parse_unicast_address(value: &str) -> Result<IpAddr, SdpParserInternalError> {
Ok(IpAddr::from_str(value)?)
pub fn parse_unicast_address(value: &str) -> Result<Address, SdpParserInternalError> {
Ok(Address::from_str(value)?)
}
#[cfg(test)]
@ -53,32 +33,30 @@ mod tests {
use super::*;
#[test]
fn test_parse_network_type() {
let internet = parse_network_type("iN");
assert!(internet.is_ok());
fn test_parse_network_type() -> Result<(), SdpParserInternalError> {
parse_network_type("iN")?;
assert!(parse_network_type("").is_err());
assert!(parse_network_type("FOO").is_err());
Ok(())
}
#[test]
fn test_parse_address_type() {
let ip4 = parse_address_type("iP4");
assert!(ip4.is_ok());
assert_eq!(ip4.unwrap(), SdpAddressType::IP4);
let ip6 = parse_address_type("Ip6");
assert!(ip6.is_ok());
assert_eq!(ip6.unwrap(), SdpAddressType::IP6);
fn test_parse_address_type() -> Result<(), SdpParserInternalError> {
let ip4 = parse_address_type("iP4")?;
assert_eq!(ip4, AddressType::IpV4);
let ip6 = parse_address_type("Ip6")?;
assert_eq!(ip6, AddressType::IpV6);
assert!(parse_address_type("").is_err());
assert!(parse_address_type("IP5").is_err());
Ok(())
}
#[test]
fn test_parse_unicast_address() {
let ip4 = parse_unicast_address("127.0.0.1");
assert!(ip4.is_ok());
let ip6 = parse_unicast_address("::1");
assert!(ip6.is_ok());
fn test_parse_unicast_address() -> Result<(), SdpParserInternalError> {
parse_unicast_address("127.0.0.1")?;
parse_unicast_address("::1")?;
Ok(())
}
}

View File

@ -7,5 +7,5 @@ authors = ["Paul Ellenbogen <pe5@cs.princeton.edu>",
[dependencies]
libc = "^0.2.0"
log = "0.4"
rsdparsa = {package = "webrtc-sdp", version = "0.1.0", path = "../rsdparsa"}
rsdparsa = {package = "webrtc-sdp", path = "../rsdparsa"}
nserror = { path = "../../../../../../xpcom/rust/nserror" }