From 1737a0b8807705c19ee0823adc92298df66ad37c Mon Sep 17 00:00:00 2001 From: yngrtc Date: Sat, 9 Mar 2024 13:16:52 -0800 Subject: [PATCH] rename CandidateBase to Candidate --- rtc-ice/src/agent/agent_selector.rs | 22 +- rtc-ice/src/agent/agent_stats.rs | 4 +- rtc-ice/src/agent/mod.rs | 44 +- rtc-ice/src/candidate/candidate_base.rs | 498 ---------------- rtc-ice/src/candidate/candidate_host.rs | 9 +- rtc-ice/src/candidate/candidate_pair_test.rs | 18 +- .../src/candidate/candidate_peer_reflexive.rs | 9 +- rtc-ice/src/candidate/candidate_relay.rs | 9 +- .../candidate/candidate_server_reflexive.rs | 9 +- rtc-ice/src/candidate/candidate_test.rs | 59 +- rtc-ice/src/candidate/mod.rs | 544 ++++++++++++++++-- 11 files changed, 582 insertions(+), 643 deletions(-) delete mode 100644 rtc-ice/src/candidate/candidate_base.rs diff --git a/rtc-ice/src/agent/agent_selector.rs b/rtc-ice/src/agent/agent_selector.rs index d8fe732..d291892 100644 --- a/rtc-ice/src/agent/agent_selector.rs +++ b/rtc-ice/src/agent/agent_selector.rs @@ -67,7 +67,7 @@ impl Agent { fn nominate_pair(&mut self) { let result = { if let Some(index) = &self.nominated_pair { - let pair = &self.checklist[*index]; + let pair = &self.candidate_pairs[*index]; // The controlling agent MUST include the USE-CANDIDATE attribute in // order to nominate a candidate pair (Section 8.1.1). The controlled // agent MUST NOT include the USE-CANDIDATE attribute in a Binding @@ -123,13 +123,13 @@ impl Agent { pub(crate) fn get_best_available_candidate_pair(&self) -> Option { let mut best: Option = None; - for (index, p) in self.checklist.iter().enumerate() { + for (index, p) in self.candidate_pairs.iter().enumerate() { if p.state == CandidatePairState::Failed { continue; } if let Some(best_index) = &mut best { - let b = &self.checklist[*best_index]; + let b = &self.candidate_pairs[*best_index]; if b.priority() < p.priority() { *best_index = index; } @@ -144,13 +144,13 @@ impl Agent { pub(crate) fn get_best_valid_candidate_pair(&self) -> Option { let mut best: Option = None; - for (index, p) in self.checklist.iter().enumerate() { + for (index, p) in self.candidate_pairs.iter().enumerate() { if p.state != CandidatePairState::Succeeded { continue; } if let Some(best_index) = &mut best { - let b = &self.checklist[*best_index]; + let b = &self.candidate_pairs[*best_index]; if b.priority() < p.priority() { *best_index = index; } @@ -233,7 +233,7 @@ impl ControllingSelector for Agent { self.nominate_pair(); } else { let has_nominated_pair = if let Some(index) = self.get_best_valid_candidate_pair() { - let p = self.checklist[index]; + let p = self.candidate_pairs[index]; self.is_nominatable(p.local, true) && self.is_nominatable(p.remote, false) } else { false @@ -241,7 +241,7 @@ impl ControllingSelector for Agent { if has_nominated_pair { if let Some(index) = self.get_best_valid_candidate_pair() { - let p = &mut self.checklist[index]; + let p = &mut self.candidate_pairs[index]; log::trace!( "Nominatable pair found, nominating ({}, {})", self.local_candidates[p.local], @@ -309,7 +309,7 @@ impl ControllingSelector for Agent { let selected_pair_is_none = self.get_selected_pair().is_none(); if let Some(index) = self.find_pair(local, remote) { - let p = &mut self.checklist[index]; + let p = &mut self.candidate_pairs[index]; p.state = CandidatePairState::Succeeded; log::trace!( "Found valid candidate pair: {}, p.state: {}, isUseCandidate: {}, {}", @@ -339,7 +339,7 @@ impl ControllingSelector for Agent { log::trace!("controllingSelector: sendBindingSuccess"); if let Some(index) = self.find_pair(local, remote) { - let p = &self.checklist[index]; + let p = &self.candidate_pairs[index]; let nominated_pair_is_none = self.nominated_pair.is_none(); log::trace!( @@ -451,7 +451,7 @@ impl ControlledSelector for Agent { ); if let Some(index) = self.find_pair(local, remote) { - let p = &mut self.checklist[index]; + let p = &mut self.candidate_pairs[index]; p.state = CandidatePairState::Succeeded; log::trace!("Found valid candidate pair: {}", *p); } else { @@ -473,7 +473,7 @@ impl ControlledSelector for Agent { } if let Some(index) = self.find_pair(local, remote) { - let p = &self.checklist[index]; + let p = &self.candidate_pairs[index]; let use_candidate = m.contains(ATTR_USE_CANDIDATE); if use_candidate { // https://tools.ietf.org/html/rfc8445#section-7.3.1.5 diff --git a/rtc-ice/src/agent/agent_stats.rs b/rtc-ice/src/agent/agent_stats.rs index 3d2a979..2c5d011 100644 --- a/rtc-ice/src/agent/agent_stats.rs +++ b/rtc-ice/src/agent/agent_stats.rs @@ -201,8 +201,8 @@ impl Default for CandidateStats { impl Agent { /// Returns a list of candidate pair stats. pub fn get_candidate_pairs_stats(&self) -> Vec { - let mut res = Vec::with_capacity(self.checklist.len()); - for cp in &self.checklist { + let mut res = Vec::with_capacity(self.candidate_pairs.len()); + for cp in &self.candidate_pairs { let stat = CandidatePairStats { timestamp: Instant::now(), local_candidate_id: self.local_candidates[cp.local].id(), diff --git a/rtc-ice/src/agent/mod.rs b/rtc-ice/src/agent/mod.rs index ece1462..25f79eb 100644 --- a/rtc-ice/src/agent/mod.rs +++ b/rtc-ice/src/agent/mod.rs @@ -12,11 +12,10 @@ use stun::attributes::*; use stun::fingerprint::*; use stun::integrity::*; use stun::message::*; -use stun::textattrs::Username; +use stun::textattrs::*; use stun::xoraddr::*; -use crate::candidate::candidate_pair::{CandidatePair, CandidatePairState}; -use crate::candidate::*; +use crate::candidate::{candidate_pair::*, *}; use crate::rand::*; use crate::state::*; use crate::url::*; @@ -78,17 +77,16 @@ pub struct Agent { pub(crate) start_time: Instant, - pub(crate) nominated_pair: Option, - pub(crate) selected_pair: Option, - pub(crate) checklist: Vec, - pub(crate) connection_state: ConnectionState, //pub(crate) started_ch_tx: Mutex>>, pub(crate) ufrag_pwd: UfragPwd, - pub(crate) local_candidates: Vec>, - pub(crate) remote_candidates: Vec>, + pub(crate) local_candidates: Vec, + pub(crate) remote_candidates: Vec, + pub(crate) candidate_pairs: Vec, + pub(crate) nominated_pair: Option, + pub(crate) selected_pair: Option, // LRU of outbound Binding request Transaction IDs pub(crate) pending_binding_requests: Vec, @@ -146,7 +144,7 @@ impl Agent { nominated_pair: None, selected_pair: None, - checklist: vec![], + candidate_pairs: vec![], connection_state: ConnectionState::New, @@ -221,9 +219,9 @@ impl Agent { } /// Adds a new local candidate. - pub fn add_local_candidate(&mut self, c: Box) -> Result<()> { + pub fn add_local_candidate(&mut self, c: Candidate) -> Result<()> { for cand in &self.local_candidates { - if cand.equal(&*c) { + if cand.equal(&c) { return Ok(()); } } @@ -240,7 +238,7 @@ impl Agent { } /// Adds a new remote candidate. - pub fn add_remote_candidate(&mut self, c: Box) -> Result<()> { + pub fn add_remote_candidate(&mut self, c: Candidate) -> Result<()> { // If we have a mDNS Candidate lets fully resolve it before adding it locally if c.candidate_type() == CandidateType::Host && c.address().ends_with(".local") { log::warn!( @@ -251,7 +249,7 @@ impl Agent { } for cand in &self.remote_candidates { - if cand.equal(&*c) { + if cand.equal(&c) { return Ok(()); } } @@ -344,7 +342,7 @@ impl Agent { self.pending_binding_requests = vec![]; - self.checklist = vec![]; + self.candidate_pairs = vec![]; self.set_selected_pair(None); self.delete_all_candidates(keep_local_candidates); @@ -360,7 +358,7 @@ impl Agent { } // Returns the local candidates. - pub(crate) fn get_local_candidates(&self) -> &[Box] { + pub(crate) fn get_local_candidates(&self) -> &[Candidate] { &self.local_candidates } @@ -518,10 +516,10 @@ impl Agent { log::trace!( "[{}]: Set selected candidate pair: {:?}", self.get_name(), - self.checklist[index] + self.candidate_pairs[index] ); - let p = &mut self.checklist[index]; + let p = &mut self.candidate_pairs[index]; p.nominated = true; self.selected_pair = Some(index); @@ -552,7 +550,7 @@ impl Agent { { let name = self.get_name().to_string(); - let checklist = &mut self.checklist; + let checklist = &mut self.candidate_pairs; if checklist.is_empty() { log::warn!( "[{}]: pingAllCandidates called with no candidate pairs. Connection is not possible yet.", @@ -595,11 +593,11 @@ impl Agent { self.remote_candidates[remote].priority(), self.is_controlling, ); - self.checklist.push(p); + self.candidate_pairs.push(p); } pub(crate) fn find_pair(&self, local: usize, remote: usize) -> Option { - let checklist = &self.checklist; + let checklist = &self.candidate_pairs; for (index, p) in checklist.iter().enumerate() { if p.local == local && p.remote == remote { return Some(index); @@ -615,7 +613,7 @@ impl Agent { self.selected_pair.as_ref().map_or_else( || (false, Duration::from_secs(0)), |selected_pair| { - let remote = self.checklist[*selected_pair].remote; + let remote = self.candidate_pairs[*selected_pair].remote; let disconnected_time = Instant::now() .duration_since(self.remote_candidates[remote].last_received()); @@ -654,7 +652,7 @@ impl Agent { self.selected_pair .as_ref() .map_or((None, None), |selected_pair| { - let p = &self.checklist[*selected_pair]; + let p = &self.candidate_pairs[*selected_pair]; (Some(p.local), Some(p.remote)) }) }; diff --git a/rtc-ice/src/candidate/candidate_base.rs b/rtc-ice/src/candidate/candidate_base.rs deleted file mode 100644 index 7aadfaf..0000000 --- a/rtc-ice/src/candidate/candidate_base.rs +++ /dev/null @@ -1,498 +0,0 @@ -use bytes::BytesMut; -use crc::{Crc, CRC_32_ISCSI}; -use std::collections::VecDeque; -use std::fmt; -use std::time::Instant; - -use super::*; -use crate::candidate::candidate_host::CandidateHostConfig; -use crate::candidate::candidate_peer_reflexive::CandidatePeerReflexiveConfig; -use crate::candidate::candidate_relay::CandidateRelayConfig; -use crate::candidate::candidate_server_reflexive::CandidateServerReflexiveConfig; -use crate::network_type::determine_network_type; -use shared::error::*; -use stun::Transmit; - -#[derive(Default)] -pub struct CandidateBaseConfig { - pub candidate_id: String, - pub network: String, - pub address: String, - pub port: u16, - pub component: u16, - pub priority: u32, - pub foundation: String, - //todo: pub initialized_ch: Option>, -} - -pub struct CandidateBase { - pub(crate) id: String, - pub(crate) network_type: NetworkType, - pub(crate) candidate_type: CandidateType, - - pub(crate) component: u16, - pub(crate) address: String, - pub(crate) port: u16, - pub(crate) related_address: Option, - pub(crate) tcp_type: TcpType, - - pub(crate) resolved_addr: SocketAddr, - pub(crate) transmits: VecDeque, - - pub(crate) last_sent: Instant, - pub(crate) last_received: Instant, - - //todo: pub(crate) closed_ch: Arc>>>, - pub(crate) foundation_override: String, - pub(crate) priority_override: u32, - - //CandidateHost - pub(crate) network: String, - //CandidateRelay - //TODO: pub(crate) relay_client: Option>, -} - -impl Default for CandidateBase { - fn default() -> Self { - Self { - id: String::new(), - network_type: NetworkType::Unspecified, - candidate_type: CandidateType::default(), - - component: 0, - address: String::new(), - port: 0, - related_address: None, - tcp_type: TcpType::default(), - - resolved_addr: SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 0), - transmits: VecDeque::new(), - - last_sent: Instant::now(), - last_received: Instant::now(), - - foundation_override: String::new(), - priority_override: 0, - network: String::new(), - //TODO: relay_client: None, - } - } -} - -// String makes the candidateBase printable -impl fmt::Display for CandidateBase { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(related_address) = self.related_address() { - write!( - f, - "{} {} {}:{}{}", - self.network_type(), - self.candidate_type(), - self.address(), - self.port(), - related_address, - ) - } else { - write!( - f, - "{} {} {}:{}", - self.network_type(), - self.candidate_type(), - self.address(), - self.port(), - ) - } - } -} - -impl Candidate for CandidateBase { - fn foundation(&self) -> String { - if !self.foundation_override.is_empty() { - return self.foundation_override.clone(); - } - - let mut buf = vec![]; - buf.extend_from_slice(self.candidate_type().to_string().as_bytes()); - buf.extend_from_slice(self.address.as_bytes()); - buf.extend_from_slice(self.network_type().to_string().as_bytes()); - - let checksum = Crc::::new(&CRC_32_ISCSI).checksum(&buf); - - format!("{checksum}") - } - - /// Returns Candidate ID. - fn id(&self) -> String { - self.id.clone() - } - - /// Returns candidate component. - fn component(&self) -> u16 { - self.component - } - - fn set_component(&mut self, component: u16) { - self.component = component; - } - - /// Returns a time indicating the last time this candidate was received. - fn last_received(&self) -> Instant { - self.last_received - } - - /// Returns a time indicating the last time this candidate was sent. - fn last_sent(&self) -> Instant { - self.last_sent - } - - /// Returns candidate NetworkType. - fn network_type(&self) -> NetworkType { - self.network_type - } - - /// Returns Candidate Address. - fn address(&self) -> String { - self.address.clone() - } - - /// Returns Candidate Port. - fn port(&self) -> u16 { - self.port - } - - /// Computes the priority for this ICE Candidate. - fn priority(&self) -> u32 { - if self.priority_override != 0 { - return self.priority_override; - } - - // The local preference MUST be an integer from 0 (lowest preference) to - // 65535 (highest preference) inclusive. When there is only a single IP - // address, this value SHOULD be set to 65535. If there are multiple - // candidates for a particular component for a particular data stream - // that have the same type, the local preference MUST be unique for each - // one. - (1 << 24) * u32::from(self.candidate_type().preference()) - + (1 << 8) * u32::from(self.local_preference()) - + (256 - u32::from(self.component())) - } - - /// Returns `Option`. - fn related_address(&self) -> Option { - self.related_address.as_ref().cloned() - } - - /// Returns candidate type. - fn candidate_type(&self) -> CandidateType { - self.candidate_type - } - - fn tcp_type(&self) -> TcpType { - self.tcp_type - } - - /// Returns the string representation of the ICECandidate. - fn marshal(&self) -> String { - let mut val = format!( - "{} {} {} {} {} {} typ {}", - self.foundation(), - self.component(), - self.network_type().network_short(), - self.priority(), - self.address(), - self.port(), - self.candidate_type() - ); - - if self.tcp_type != TcpType::Unspecified { - val += format!(" tcptype {}", self.tcp_type()).as_str(); - } - - if let Some(related_address) = self.related_address() { - val += format!( - " raddr {} rport {}", - related_address.address, related_address.port, - ) - .as_str(); - } - - val - } - - fn addr(&self) -> SocketAddr { - self.resolved_addr - } - - /// Stops the recvLoop. - fn close(&self) -> Result<()> { - /*TODO:{ - let mut closed_ch = self.closed_ch.lock().await; - if closed_ch.is_none() { - return Err(Error::ErrClosed); - } - closed_ch.take(); - }*/ - - Ok(()) - } - - fn seen(&mut self, outbound: bool) { - let now = Instant::now(); - - if outbound { - self.set_last_sent(now); - } else { - self.set_last_received(now); - } - } - - fn write(&mut self, raw: &[u8], remote: SocketAddr) -> Result { - let n = raw.len(); - self.transmits.push_back(Transmit { - now: Instant::now(), - remote, - ecn: None, - local_ip: Some(self.resolved_addr.ip()), - payload: BytesMut::from(raw), - }); - - self.seen(true); - Ok(n) - } - - fn poll_transmit(&mut self) -> Option { - self.transmits.pop_front() - } - - /// Used to compare two candidateBases. - fn equal(&self, other: &dyn Candidate) -> bool { - self.network_type() == other.network_type() - && self.candidate_type() == other.candidate_type() - && self.address() == other.address() - && self.port() == other.port() - && self.tcp_type() == other.tcp_type() - && self.related_address() == other.related_address() - } - - fn set_ip(&mut self, ip: &IpAddr) -> Result<()> { - let network_type = determine_network_type(&self.network, ip)?; - self.network_type = network_type; - self.resolved_addr = SocketAddr::new(*ip, self.port); - - Ok(()) - } - - /*TODO:fn get_closed_ch(&self) -> Arc>>> { - self.closed_ch.clone() - }*/ -} - -impl CandidateBase { - pub fn set_last_received(&mut self, now: Instant) { - self.last_received = now; - } - - pub fn set_last_sent(&mut self, now: Instant) { - self.last_sent = now; - } - - /// Returns the local preference for this candidate. - pub fn local_preference(&self) -> u16 { - if self.network_type().is_tcp() { - // RFC 6544, section 4.2 - // - // In Section 4.1.2.1 of [RFC5245], a recommended formula for UDP ICE - // candidate prioritization is defined. For TCP candidates, the same - // formula and candidate type preferences SHOULD be used, and the - // RECOMMENDED type preferences for the new candidate types defined in - // this document (see Section 5) are 105 for NAT-assisted candidates and - // 75 for UDP-tunneled candidates. - // - // (...) - // - // With TCP candidates, the local preference part of the recommended - // priority formula is updated to also include the directionality - // (active, passive, or simultaneous-open) of the TCP connection. The - // RECOMMENDED local preference is then defined as: - // - // local preference = (2^13) * direction-pref + other-pref - // - // The direction-pref MUST be between 0 and 7 (both inclusive), with 7 - // being the most preferred. The other-pref MUST be between 0 and 8191 - // (both inclusive), with 8191 being the most preferred. It is - // RECOMMENDED that the host, UDP-tunneled, and relayed TCP candidates - // have the direction-pref assigned as follows: 6 for active, 4 for - // passive, and 2 for S-O. For the NAT-assisted and server reflexive - // candidates, the RECOMMENDED values are: 6 for S-O, 4 for active, and - // 2 for passive. - // - // (...) - // - // If any two candidates have the same type-preference and direction- - // pref, they MUST have a unique other-pref. With this specification, - // this usually only happens with multi-homed hosts, in which case - // other-pref is the preference for the particular IP address from which - // the candidate was obtained. When there is only a single IP address, - // this value SHOULD be set to the maximum allowed value (8191). - let other_pref: u16 = 8191; - - let direction_pref: u16 = match self.candidate_type() { - CandidateType::Host | CandidateType::Relay => match self.tcp_type() { - TcpType::Active => 6, - TcpType::Passive => 4, - TcpType::SimultaneousOpen => 2, - TcpType::Unspecified => 0, - }, - CandidateType::PeerReflexive | CandidateType::ServerReflexive => { - match self.tcp_type() { - TcpType::SimultaneousOpen => 6, - TcpType::Active => 4, - TcpType::Passive => 2, - TcpType::Unspecified => 0, - } - } - CandidateType::Unspecified => 0, - }; - - (1 << 13) * direction_pref + other_pref - } else { - DEFAULT_LOCAL_PREFERENCE - } - } -} - -/// Creates a Candidate from its string representation. -pub fn unmarshal_candidate(raw: &str) -> Result { - let split: Vec<&str> = raw.split_whitespace().collect(); - if split.len() < 8 { - return Err(Error::Other(format!( - "{:?} ({})", - Error::ErrAttributeTooShortIceCandidate, - split.len() - ))); - } - - // Foundation - let foundation = split[0].to_owned(); - - // Component - let component: u16 = split[1].parse()?; - - // Network - let network = split[2].to_owned(); - - // Priority - let priority: u32 = split[3].parse()?; - - // Address - let address = split[4].to_owned(); - - // Port - let port: u16 = split[5].parse()?; - - let typ = split[7]; - - let mut rel_addr = String::new(); - let mut rel_port = 0; - let mut tcp_type = TcpType::Unspecified; - - if split.len() > 8 { - let split2 = &split[8..]; - - if split2[0] == "raddr" { - if split2.len() < 4 { - return Err(Error::Other(format!( - "{:?}: incorrect length", - Error::ErrParseRelatedAddr - ))); - } - - // RelatedAddress - rel_addr = split2[1].to_owned(); - - // RelatedPort - rel_port = split2[3].parse()?; - } else if split2[0] == "tcptype" { - if split2.len() < 2 { - return Err(Error::Other(format!( - "{:?}: incorrect length", - Error::ErrParseType - ))); - } - - tcp_type = TcpType::from(split2[1]); - } - } - - match typ { - "host" => { - let config = CandidateHostConfig { - base_config: CandidateBaseConfig { - network, - address, - port, - component, - priority, - foundation, - ..CandidateBaseConfig::default() - }, - tcp_type, - }; - config.new_candidate_host() - } - "srflx" => { - let config = CandidateServerReflexiveConfig { - base_config: CandidateBaseConfig { - network, - address, - port, - component, - priority, - foundation, - ..CandidateBaseConfig::default() - }, - rel_addr, - rel_port, - }; - config.new_candidate_server_reflexive() - } - "prflx" => { - let config = CandidatePeerReflexiveConfig { - base_config: CandidateBaseConfig { - network, - address, - port, - component, - priority, - foundation, - ..CandidateBaseConfig::default() - }, - rel_addr, - rel_port, - }; - - config.new_candidate_peer_reflexive() - } - "relay" => { - let config = CandidateRelayConfig { - base_config: CandidateBaseConfig { - network, - address, - port, - component, - priority, - foundation, - ..CandidateBaseConfig::default() - }, - rel_addr, - rel_port, - }; - config.new_candidate_relay() - } - _ => Err(Error::Other(format!( - "{:?} ({})", - Error::ErrUnknownCandidateType, - typ - ))), - } -} diff --git a/rtc-ice/src/candidate/candidate_host.rs b/rtc-ice/src/candidate/candidate_host.rs index 089e2d3..8bde12f 100644 --- a/rtc-ice/src/candidate/candidate_host.rs +++ b/rtc-ice/src/candidate/candidate_host.rs @@ -1,24 +1,23 @@ -use super::candidate_base::*; use super::*; use crate::rand::generate_cand_id; /// The config required to create a new `CandidateHost`. #[derive(Default)] pub struct CandidateHostConfig { - pub base_config: CandidateBaseConfig, + pub base_config: CandidateConfig, pub tcp_type: TcpType, } impl CandidateHostConfig { /// Creates a new host candidate. - pub fn new_candidate_host(self) -> Result { + pub fn new_candidate_host(self) -> Result { let mut candidate_id = self.base_config.candidate_id; if candidate_id.is_empty() { candidate_id = generate_cand_id(); } - let mut c = CandidateBase { + let mut c = Candidate { id: candidate_id, address: self.base_config.address.clone(), candidate_type: CandidateType::Host, @@ -30,7 +29,7 @@ impl CandidateHostConfig { network: self.base_config.network, network_type: NetworkType::Udp4, //conn: self.base_config.conn, - ..CandidateBase::default() + ..Candidate::default() }; if !self.base_config.address.ends_with(".local") { diff --git a/rtc-ice/src/candidate/candidate_pair_test.rs b/rtc-ice/src/candidate/candidate_pair_test.rs index 4276f4e..09c687e 100644 --- a/rtc-ice/src/candidate/candidate_pair_test.rs +++ b/rtc-ice/src/candidate/candidate_pair_test.rs @@ -1,14 +1,14 @@ use super::*; -use crate::candidate::candidate_base::{CandidateBase, CandidateBaseConfig}; use crate::candidate::candidate_host::CandidateHostConfig; use crate::candidate::candidate_pair::CandidatePair; use crate::candidate::candidate_peer_reflexive::CandidatePeerReflexiveConfig; use crate::candidate::candidate_relay::CandidateRelayConfig; use crate::candidate::candidate_server_reflexive::CandidateServerReflexiveConfig; +use crate::candidate::{Candidate, CandidateConfig}; -pub(crate) fn host_candidate() -> Result { +pub(crate) fn host_candidate() -> Result { CandidateHostConfig { - base_config: CandidateBaseConfig { + base_config: CandidateConfig { network: "udp".to_owned(), address: "0.0.0.0".to_owned(), component: COMPONENT_RTP, @@ -19,9 +19,9 @@ pub(crate) fn host_candidate() -> Result { .new_candidate_host() } -pub(crate) fn prflx_candidate() -> Result { +pub(crate) fn prflx_candidate() -> Result { CandidatePeerReflexiveConfig { - base_config: CandidateBaseConfig { + base_config: CandidateConfig { network: "udp".to_owned(), address: "0.0.0.0".to_owned(), component: COMPONENT_RTP, @@ -32,9 +32,9 @@ pub(crate) fn prflx_candidate() -> Result { .new_candidate_peer_reflexive() } -pub(crate) fn srflx_candidate() -> Result { +pub(crate) fn srflx_candidate() -> Result { CandidateServerReflexiveConfig { - base_config: CandidateBaseConfig { + base_config: CandidateConfig { network: "udp".to_owned(), address: "0.0.0.0".to_owned(), component: COMPONENT_RTP, @@ -45,9 +45,9 @@ pub(crate) fn srflx_candidate() -> Result { .new_candidate_server_reflexive() } -pub(crate) fn relay_candidate() -> Result { +pub(crate) fn relay_candidate() -> Result { CandidateRelayConfig { - base_config: CandidateBaseConfig { + base_config: CandidateConfig { network: "udp".to_owned(), address: "0.0.0.0".to_owned(), component: COMPONENT_RTP, diff --git a/rtc-ice/src/candidate/candidate_peer_reflexive.rs b/rtc-ice/src/candidate/candidate_peer_reflexive.rs index cf864b5..94ed592 100644 --- a/rtc-ice/src/candidate/candidate_peer_reflexive.rs +++ b/rtc-ice/src/candidate/candidate_peer_reflexive.rs @@ -1,4 +1,3 @@ -use super::candidate_base::*; use super::*; use crate::network_type::determine_network_type; use crate::rand::generate_cand_id; @@ -7,7 +6,7 @@ use shared::error::*; /// The config required to create a new `CandidatePeerReflexive`. #[derive(Default)] pub struct CandidatePeerReflexiveConfig { - pub base_config: CandidateBaseConfig, + pub base_config: CandidateConfig, pub rel_addr: String, pub rel_port: u16, @@ -15,7 +14,7 @@ pub struct CandidatePeerReflexiveConfig { impl CandidatePeerReflexiveConfig { /// Creates a new peer reflective candidate. - pub fn new_candidate_peer_reflexive(self) -> Result { + pub fn new_candidate_peer_reflexive(self) -> Result { let ip: IpAddr = match self.base_config.address.parse() { Ok(ip) => ip, Err(_) => return Err(Error::ErrAddressParseFailed), @@ -27,7 +26,7 @@ impl CandidatePeerReflexiveConfig { candidate_id = generate_cand_id(); } - let c = CandidateBase { + let c = Candidate { id: candidate_id, network_type, candidate_type: CandidateType::PeerReflexive, @@ -42,7 +41,7 @@ impl CandidatePeerReflexiveConfig { port: self.rel_port, }), //TODO:conn: self.base_config.conn, - ..CandidateBase::default() + ..Candidate::default() }; Ok(c) diff --git a/rtc-ice/src/candidate/candidate_relay.rs b/rtc-ice/src/candidate/candidate_relay.rs index fa7b8dd..bab10ba 100644 --- a/rtc-ice/src/candidate/candidate_relay.rs +++ b/rtc-ice/src/candidate/candidate_relay.rs @@ -1,4 +1,3 @@ -use super::candidate_base::*; use super::*; use crate::network_type::determine_network_type; use crate::rand::generate_cand_id; @@ -7,7 +6,7 @@ use shared::error::*; /// The config required to create a new `CandidateRelay`. #[derive(Default)] pub struct CandidateRelayConfig { - pub base_config: CandidateBaseConfig, + pub base_config: CandidateConfig, pub rel_addr: String, pub rel_port: u16, @@ -16,7 +15,7 @@ pub struct CandidateRelayConfig { impl CandidateRelayConfig { /// Creates a new relay candidate. - pub fn new_candidate_relay(self) -> Result { + pub fn new_candidate_relay(self) -> Result { let mut candidate_id = self.base_config.candidate_id; if candidate_id.is_empty() { candidate_id = generate_cand_id(); @@ -28,7 +27,7 @@ impl CandidateRelayConfig { }; let network_type = determine_network_type(&self.base_config.network, &ip)?; - let c = CandidateBase { + let c = Candidate { id: candidate_id, network_type, candidate_type: CandidateType::Relay, @@ -44,7 +43,7 @@ impl CandidateRelayConfig { }), //TODO:conn: self.base_config.conn, //TODO: relay_client: self.relay_client.clone(), - ..CandidateBase::default() + ..Candidate::default() }; Ok(c) diff --git a/rtc-ice/src/candidate/candidate_server_reflexive.rs b/rtc-ice/src/candidate/candidate_server_reflexive.rs index ce6a1b6..373c180 100644 --- a/rtc-ice/src/candidate/candidate_server_reflexive.rs +++ b/rtc-ice/src/candidate/candidate_server_reflexive.rs @@ -1,4 +1,3 @@ -use super::candidate_base::*; use super::*; use crate::network_type::determine_network_type; use crate::rand::generate_cand_id; @@ -7,7 +6,7 @@ use shared::error::*; /// The config required to create a new `CandidateServerReflexive`. #[derive(Default)] pub struct CandidateServerReflexiveConfig { - pub base_config: CandidateBaseConfig, + pub base_config: CandidateConfig, pub rel_addr: String, pub rel_port: u16, @@ -15,7 +14,7 @@ pub struct CandidateServerReflexiveConfig { impl CandidateServerReflexiveConfig { /// Creates a new server reflective candidate. - pub fn new_candidate_server_reflexive(self) -> Result { + pub fn new_candidate_server_reflexive(self) -> Result { let ip: IpAddr = match self.base_config.address.parse() { Ok(ip) => ip, Err(_) => return Err(Error::ErrAddressParseFailed), @@ -27,7 +26,7 @@ impl CandidateServerReflexiveConfig { candidate_id = generate_cand_id(); } - let c = CandidateBase { + let c = Candidate { id: candidate_id, network_type, candidate_type: CandidateType::ServerReflexive, @@ -42,7 +41,7 @@ impl CandidateServerReflexiveConfig { port: self.rel_port, }), //TODO: conn: self.base_config.conn, - ..CandidateBase::default() + ..Candidate::default() }; Ok(c) diff --git a/rtc-ice/src/candidate/candidate_test.rs b/rtc-ice/src/candidate/candidate_test.rs index 2011583..464a64b 100644 --- a/rtc-ice/src/candidate/candidate_test.rs +++ b/rtc-ice/src/candidate/candidate_test.rs @@ -1,12 +1,13 @@ use super::*; -use crate::candidate::candidate_base::{unmarshal_candidate, CandidateBase}; use crate::candidate::candidate_pair::CandidatePairState; +use crate::candidate::{unmarshal_candidate, Candidate}; +use std::time::Instant; #[test] fn test_candidate_priority() -> Result<()> { let tests = vec![ ( - CandidateBase { + Candidate { candidate_type: CandidateType::Host, component: COMPONENT_RTP, ..Default::default() @@ -14,7 +15,7 @@ fn test_candidate_priority() -> Result<()> { 2130706431, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::Host, component: COMPONENT_RTP, network_type: NetworkType::Tcp4, @@ -24,7 +25,7 @@ fn test_candidate_priority() -> Result<()> { 2128609279, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::Host, component: COMPONENT_RTP, network_type: NetworkType::Tcp4, @@ -34,7 +35,7 @@ fn test_candidate_priority() -> Result<()> { 2124414975, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::Host, component: COMPONENT_RTP, network_type: NetworkType::Tcp4, @@ -44,7 +45,7 @@ fn test_candidate_priority() -> Result<()> { 2120220671, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::PeerReflexive, component: COMPONENT_RTP, ..Default::default() @@ -52,7 +53,7 @@ fn test_candidate_priority() -> Result<()> { 1862270975, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::PeerReflexive, component: COMPONENT_RTP, network_type: NetworkType::Tcp6, @@ -62,7 +63,7 @@ fn test_candidate_priority() -> Result<()> { 1860173823, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::PeerReflexive, component: COMPONENT_RTP, network_type: NetworkType::Tcp6, @@ -72,7 +73,7 @@ fn test_candidate_priority() -> Result<()> { 1855979519, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::PeerReflexive, component: COMPONENT_RTP, network_type: NetworkType::Tcp6, @@ -82,7 +83,7 @@ fn test_candidate_priority() -> Result<()> { 1851785215, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::ServerReflexive, component: COMPONENT_RTP, ..Default::default() @@ -90,7 +91,7 @@ fn test_candidate_priority() -> Result<()> { 1694498815, ), ( - CandidateBase { + Candidate { candidate_type: CandidateType::Relay, component: COMPONENT_RTP, ..Default::default() @@ -112,7 +113,7 @@ fn test_candidate_priority() -> Result<()> { #[test] fn test_candidate_last_sent() -> Result<()> { - let mut candidate = CandidateBase::default(); + let mut candidate = Candidate::default(); let now = Instant::now(); candidate.set_last_sent(now); @@ -123,7 +124,7 @@ fn test_candidate_last_sent() -> Result<()> { #[test] fn test_candidate_last_received() -> Result<()> { - let mut candidate = CandidateBase::default(); + let mut candidate = Candidate::default(); let now = Instant::now(); candidate.set_last_received(now); @@ -136,14 +137,14 @@ fn test_candidate_last_received() -> Result<()> { fn test_candidate_foundation() -> Result<()> { // All fields are the same assert_eq!( - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp4, address: "A".to_owned(), ..Default::default() }) .foundation(), - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp4, address: "A".to_owned(), @@ -154,14 +155,14 @@ fn test_candidate_foundation() -> Result<()> { // Different Address assert_ne!( - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp4, address: "A".to_owned(), ..Default::default() }) .foundation(), - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp4, address: "B".to_owned(), @@ -172,14 +173,14 @@ fn test_candidate_foundation() -> Result<()> { // Different networkType assert_ne!( - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp4, address: "A".to_owned(), ..Default::default() }) .foundation(), - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp6, address: "A".to_owned(), @@ -190,14 +191,14 @@ fn test_candidate_foundation() -> Result<()> { // Different candidateType assert_ne!( - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp4, address: "A".to_owned(), ..Default::default() }) .foundation(), - (CandidateBase { + (Candidate { candidate_type: CandidateType::PeerReflexive, network_type: NetworkType::Udp4, address: "A".to_owned(), @@ -208,7 +209,7 @@ fn test_candidate_foundation() -> Result<()> { // Port has no effect assert_eq!( - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp4, address: "A".to_owned(), @@ -216,7 +217,7 @@ fn test_candidate_foundation() -> Result<()> { ..Default::default() }) .foundation(), - (CandidateBase { + (Candidate { candidate_type: CandidateType::Host, network_type: NetworkType::Udp4, address: "A".to_owned(), @@ -299,7 +300,7 @@ fn test_candidate_type_to_string() { fn test_candidate_marshal() -> Result<()> { let tests = vec![ ( - Some(CandidateBase{ + Some(Candidate{ network_type: NetworkType::Udp6, candidate_type: CandidateType::Host, address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a".to_owned(), @@ -311,7 +312,7 @@ fn test_candidate_marshal() -> Result<()> { "750 1 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a 53987 typ host", ), ( - Some(CandidateBase{ + Some(Candidate{ network_type: NetworkType::Udp4, candidate_type: CandidateType::Host, address: "10.0.75.1".to_owned(), @@ -321,7 +322,7 @@ fn test_candidate_marshal() -> Result<()> { "4273957277 1 udp 2130706431 10.0.75.1 53634 typ host", ), ( - Some(CandidateBase{ + Some(Candidate{ network_type: NetworkType::Udp4, candidate_type: CandidateType::ServerReflexive, address: "191.228.238.68".to_owned(), @@ -335,7 +336,7 @@ fn test_candidate_marshal() -> Result<()> { "647372371 1 udp 1694498815 191.228.238.68 53991 typ srflx raddr 192.168.0.274 rport 53991", ), ( - Some(CandidateBase{ + Some(Candidate{ network_type: NetworkType::Udp4, candidate_type: CandidateType::Relay, address: "50.0.0.1".to_owned(), @@ -350,7 +351,7 @@ fn test_candidate_marshal() -> Result<()> { "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 5001", ), ( - Some(CandidateBase{ + Some(Candidate{ network_type: NetworkType::Tcp4, candidate_type: CandidateType::Host, address: "192.168.0.196".to_owned(), @@ -361,7 +362,7 @@ fn test_candidate_marshal() -> Result<()> { "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active", ), ( - Some(CandidateBase{ + Some(Candidate{ network_type: NetworkType::Udp4, candidate_type: CandidateType::Host, address: "e2494022-4d9a-4c1e-a750-cc48d4f8d6ee.local".to_owned(), diff --git a/rtc-ice/src/candidate/mod.rs b/rtc-ice/src/candidate/mod.rs index 894cc65..2d7ed8b 100644 --- a/rtc-ice/src/candidate/mod.rs +++ b/rtc-ice/src/candidate/mod.rs @@ -9,7 +9,6 @@ mod candidate_test; mod candidate_server_reflexive_test; */ -pub mod candidate_base; pub mod candidate_host; pub mod candidate_pair; pub mod candidate_peer_reflexive; @@ -18,11 +17,20 @@ pub mod candidate_server_reflexive; use crate::network_type::NetworkType; use crate::tcp_type::TcpType; +use bytes::BytesMut; +use crc::{Crc, CRC_32_ISCSI}; use serde::Serialize; use shared::error::*; +use std::collections::VecDeque; use std::fmt; use std::net::{IpAddr, SocketAddr}; use std::time::Instant; + +use crate::candidate::candidate_host::CandidateHostConfig; +use crate::candidate::candidate_peer_reflexive::CandidatePeerReflexiveConfig; +use crate::candidate::candidate_relay::CandidateRelayConfig; +use crate::candidate::candidate_server_reflexive::CandidateServerReflexiveConfig; +use crate::network_type::determine_network_type; use stun::Transmit; pub(crate) const RECEIVE_MTU: usize = 8192; @@ -33,56 +41,6 @@ pub(crate) const COMPONENT_RTP: u16 = 1; /// Indicates that the candidate is used for RTCP. pub(crate) const COMPONENT_RTCP: u16 = 0; -/// Candidate represents an ICE candidate -pub trait Candidate: fmt::Display { - /// An arbitrary string used in the freezing algorithm to - /// group similar candidates. It is the same for two candidates that - /// have the same type, base IP address, protocol (UDP, TCP, etc.), - /// and STUN or TURN server. - fn foundation(&self) -> String; - - /// A unique identifier for just this candidate - /// Unlike the foundation this is different for each candidate. - fn id(&self) -> String; - - /// A component is a piece of a data stream. - /// An example is one for RTP, and one for RTCP - fn component(&self) -> u16; - fn set_component(&mut self, c: u16); - - /// The last time this candidate received traffic - fn last_received(&self) -> Instant; - - /// The last time this candidate sent traffic - fn last_sent(&self) -> Instant; - - fn network_type(&self) -> NetworkType; - fn address(&self) -> String; - fn port(&self) -> u16; - - fn priority(&self) -> u32; - - /// A transport address related to candidate, - /// which is useful for diagnostics and other purposes. - fn related_address(&self) -> Option; - - fn candidate_type(&self) -> CandidateType; - fn tcp_type(&self) -> TcpType; - - fn marshal(&self) -> String; - - fn addr(&self) -> SocketAddr; - - fn close(&self) -> Result<()>; - fn seen(&mut self, outbound: bool); - - fn write(&mut self, raw: &[u8], remote_addr: SocketAddr) -> Result; - fn poll_transmit(&mut self) -> Option; - - fn equal(&self, other: &dyn Candidate) -> bool; - fn set_ip(&mut self, ip: &IpAddr) -> Result<()>; -} - /// Represents the type of candidate `CandidateType` enum. #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] pub enum CandidateType { @@ -164,3 +122,487 @@ impl fmt::Display for CandidateRelatedAddress { write!(f, " related {}:{}", self.address, self.port) } } + +#[derive(Default)] +pub struct CandidateConfig { + pub candidate_id: String, + pub network: String, + pub address: String, + pub port: u16, + pub component: u16, + pub priority: u32, + pub foundation: String, + //todo: pub initialized_ch: Option>, +} + +pub struct Candidate { + pub(crate) id: String, + pub(crate) network_type: NetworkType, + pub(crate) candidate_type: CandidateType, + + pub(crate) component: u16, + pub(crate) address: String, + pub(crate) port: u16, + pub(crate) related_address: Option, + pub(crate) tcp_type: TcpType, + + pub(crate) resolved_addr: SocketAddr, + pub(crate) transmits: VecDeque, + + pub(crate) last_sent: Instant, + pub(crate) last_received: Instant, + + //todo: pub(crate) closed_ch: Arc>>>, + pub(crate) foundation_override: String, + pub(crate) priority_override: u32, + + //CandidateHost + pub(crate) network: String, + //CandidateRelay + //TODO: pub(crate) relay_client: Option>, +} + +impl Default for Candidate { + fn default() -> Self { + Self { + id: String::new(), + network_type: NetworkType::Unspecified, + candidate_type: CandidateType::default(), + + component: 0, + address: String::new(), + port: 0, + related_address: None, + tcp_type: TcpType::default(), + + resolved_addr: SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 0), + transmits: VecDeque::new(), + + last_sent: Instant::now(), + last_received: Instant::now(), + + foundation_override: String::new(), + priority_override: 0, + network: String::new(), + //TODO: relay_client: None, + } + } +} + +// String makes the candidateBase printable +impl fmt::Display for Candidate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(related_address) = self.related_address() { + write!( + f, + "{} {} {}:{}{}", + self.network_type(), + self.candidate_type(), + self.address(), + self.port(), + related_address, + ) + } else { + write!( + f, + "{} {} {}:{}", + self.network_type(), + self.candidate_type(), + self.address(), + self.port(), + ) + } + } +} + +impl Candidate { + pub fn foundation(&self) -> String { + if !self.foundation_override.is_empty() { + return self.foundation_override.clone(); + } + + let mut buf = vec![]; + buf.extend_from_slice(self.candidate_type().to_string().as_bytes()); + buf.extend_from_slice(self.address.as_bytes()); + buf.extend_from_slice(self.network_type().to_string().as_bytes()); + + let checksum = Crc::::new(&CRC_32_ISCSI).checksum(&buf); + + format!("{checksum}") + } + + /// Returns Candidate ID. + pub fn id(&self) -> String { + self.id.clone() + } + + /// Returns candidate component. + pub fn component(&self) -> u16 { + self.component + } + + pub fn set_component(&mut self, component: u16) { + self.component = component; + } + + /// Returns a time indicating the last time this candidate was received. + pub fn last_received(&self) -> Instant { + self.last_received + } + + /// Returns a time indicating the last time this candidate was sent. + pub fn last_sent(&self) -> Instant { + self.last_sent + } + + /// Returns candidate NetworkType. + pub fn network_type(&self) -> NetworkType { + self.network_type + } + + /// Returns Candidate Address. + pub fn address(&self) -> String { + self.address.clone() + } + + /// Returns Candidate Port. + pub fn port(&self) -> u16 { + self.port + } + + /// Computes the priority for this ICE Candidate. + pub fn priority(&self) -> u32 { + if self.priority_override != 0 { + return self.priority_override; + } + + // The local preference MUST be an integer from 0 (lowest preference) to + // 65535 (highest preference) inclusive. When there is only a single IP + // address, this value SHOULD be set to 65535. If there are multiple + // candidates for a particular component for a particular data stream + // that have the same type, the local preference MUST be unique for each + // one. + (1 << 24) * u32::from(self.candidate_type().preference()) + + (1 << 8) * u32::from(self.local_preference()) + + (256 - u32::from(self.component())) + } + + /// Returns `Option`. + pub fn related_address(&self) -> Option { + self.related_address.as_ref().cloned() + } + + /// Returns candidate type. + pub fn candidate_type(&self) -> CandidateType { + self.candidate_type + } + + pub fn tcp_type(&self) -> TcpType { + self.tcp_type + } + + /// Returns the string representation of the ICECandidate. + pub fn marshal(&self) -> String { + let mut val = format!( + "{} {} {} {} {} {} typ {}", + self.foundation(), + self.component(), + self.network_type().network_short(), + self.priority(), + self.address(), + self.port(), + self.candidate_type() + ); + + if self.tcp_type != TcpType::Unspecified { + val += format!(" tcptype {}", self.tcp_type()).as_str(); + } + + if let Some(related_address) = self.related_address() { + val += format!( + " raddr {} rport {}", + related_address.address, related_address.port, + ) + .as_str(); + } + + val + } + + pub fn addr(&self) -> SocketAddr { + self.resolved_addr + } + + /// Stops the recvLoop. + pub fn close(&self) -> Result<()> { + /*TODO:{ + let mut closed_ch = self.closed_ch.lock().await; + if closed_ch.is_none() { + return Err(Error::ErrClosed); + } + closed_ch.take(); + }*/ + + Ok(()) + } + + pub fn seen(&mut self, outbound: bool) { + let now = Instant::now(); + + if outbound { + self.set_last_sent(now); + } else { + self.set_last_received(now); + } + } + + pub fn write(&mut self, raw: &[u8], remote: SocketAddr) -> Result { + let n = raw.len(); + self.transmits.push_back(Transmit { + now: Instant::now(), + remote, + ecn: None, + local_ip: Some(self.resolved_addr.ip()), + payload: BytesMut::from(raw), + }); + + self.seen(true); + Ok(n) + } + + pub fn poll_transmit(&mut self) -> Option { + self.transmits.pop_front() + } + + /// Used to compare two candidateBases. + pub fn equal(&self, other: &Candidate) -> bool { + self.network_type() == other.network_type() + && self.candidate_type() == other.candidate_type() + && self.address() == other.address() + && self.port() == other.port() + && self.tcp_type() == other.tcp_type() + && self.related_address() == other.related_address() + } + + pub fn set_ip(&mut self, ip: &IpAddr) -> Result<()> { + let network_type = determine_network_type(&self.network, ip)?; + self.network_type = network_type; + self.resolved_addr = SocketAddr::new(*ip, self.port); + + Ok(()) + } + + /*TODO:fn get_closed_ch(&self) -> Arc>>> { + self.closed_ch.clone() + }*/ +} + +impl Candidate { + pub fn set_last_received(&mut self, now: Instant) { + self.last_received = now; + } + + pub fn set_last_sent(&mut self, now: Instant) { + self.last_sent = now; + } + + /// Returns the local preference for this candidate. + pub fn local_preference(&self) -> u16 { + if self.network_type().is_tcp() { + // RFC 6544, section 4.2 + // + // In Section 4.1.2.1 of [RFC5245], a recommended formula for UDP ICE + // candidate prioritization is defined. For TCP candidates, the same + // formula and candidate type preferences SHOULD be used, and the + // RECOMMENDED type preferences for the new candidate types defined in + // this document (see Section 5) are 105 for NAT-assisted candidates and + // 75 for UDP-tunneled candidates. + // + // (...) + // + // With TCP candidates, the local preference part of the recommended + // priority formula is updated to also include the directionality + // (active, passive, or simultaneous-open) of the TCP connection. The + // RECOMMENDED local preference is then defined as: + // + // local preference = (2^13) * direction-pref + other-pref + // + // The direction-pref MUST be between 0 and 7 (both inclusive), with 7 + // being the most preferred. The other-pref MUST be between 0 and 8191 + // (both inclusive), with 8191 being the most preferred. It is + // RECOMMENDED that the host, UDP-tunneled, and relayed TCP candidates + // have the direction-pref assigned as follows: 6 for active, 4 for + // passive, and 2 for S-O. For the NAT-assisted and server reflexive + // candidates, the RECOMMENDED values are: 6 for S-O, 4 for active, and + // 2 for passive. + // + // (...) + // + // If any two candidates have the same type-preference and direction- + // pref, they MUST have a unique other-pref. With this specification, + // this usually only happens with multi-homed hosts, in which case + // other-pref is the preference for the particular IP address from which + // the candidate was obtained. When there is only a single IP address, + // this value SHOULD be set to the maximum allowed value (8191). + let other_pref: u16 = 8191; + + let direction_pref: u16 = match self.candidate_type() { + CandidateType::Host | CandidateType::Relay => match self.tcp_type() { + TcpType::Active => 6, + TcpType::Passive => 4, + TcpType::SimultaneousOpen => 2, + TcpType::Unspecified => 0, + }, + CandidateType::PeerReflexive | CandidateType::ServerReflexive => { + match self.tcp_type() { + TcpType::SimultaneousOpen => 6, + TcpType::Active => 4, + TcpType::Passive => 2, + TcpType::Unspecified => 0, + } + } + CandidateType::Unspecified => 0, + }; + + (1 << 13) * direction_pref + other_pref + } else { + DEFAULT_LOCAL_PREFERENCE + } + } +} + +/// Creates a Candidate from its string representation. +pub fn unmarshal_candidate(raw: &str) -> Result { + let split: Vec<&str> = raw.split_whitespace().collect(); + if split.len() < 8 { + return Err(Error::Other(format!( + "{:?} ({})", + Error::ErrAttributeTooShortIceCandidate, + split.len() + ))); + } + + // Foundation + let foundation = split[0].to_owned(); + + // Component + let component: u16 = split[1].parse()?; + + // Network + let network = split[2].to_owned(); + + // Priority + let priority: u32 = split[3].parse()?; + + // Address + let address = split[4].to_owned(); + + // Port + let port: u16 = split[5].parse()?; + + let typ = split[7]; + + let mut rel_addr = String::new(); + let mut rel_port = 0; + let mut tcp_type = TcpType::Unspecified; + + if split.len() > 8 { + let split2 = &split[8..]; + + if split2[0] == "raddr" { + if split2.len() < 4 { + return Err(Error::Other(format!( + "{:?}: incorrect length", + Error::ErrParseRelatedAddr + ))); + } + + // RelatedAddress + rel_addr = split2[1].to_owned(); + + // RelatedPort + rel_port = split2[3].parse()?; + } else if split2[0] == "tcptype" { + if split2.len() < 2 { + return Err(Error::Other(format!( + "{:?}: incorrect length", + Error::ErrParseType + ))); + } + + tcp_type = TcpType::from(split2[1]); + } + } + + match typ { + "host" => { + let config = CandidateHostConfig { + base_config: CandidateConfig { + network, + address, + port, + component, + priority, + foundation, + ..CandidateConfig::default() + }, + tcp_type, + }; + config.new_candidate_host() + } + "srflx" => { + let config = CandidateServerReflexiveConfig { + base_config: CandidateConfig { + network, + address, + port, + component, + priority, + foundation, + ..CandidateConfig::default() + }, + rel_addr, + rel_port, + }; + config.new_candidate_server_reflexive() + } + "prflx" => { + let config = CandidatePeerReflexiveConfig { + base_config: CandidateConfig { + network, + address, + port, + component, + priority, + foundation, + ..CandidateConfig::default() + }, + rel_addr, + rel_port, + }; + + config.new_candidate_peer_reflexive() + } + "relay" => { + let config = CandidateRelayConfig { + base_config: CandidateConfig { + network, + address, + port, + component, + priority, + foundation, + ..CandidateConfig::default() + }, + rel_addr, + rel_port, + }; + config.new_candidate_relay() + } + _ => Err(Error::Other(format!( + "{:?} ({})", + Error::ErrUnknownCandidateType, + typ + ))), + } +}