rtc_ice/candidate/
mod.rs

1#[cfg(test)]
2mod candidate_pair_test;
3#[cfg(test)]
4mod candidate_test;
5
6//TODO: #[cfg(test)]
7//TODO: mod candidate_relay_test;
8/*TODO: #[cfg(test)]
9TODO: mod candidate_server_reflexive_test;
10*/
11
12pub mod candidate_host;
13pub mod candidate_pair;
14pub mod candidate_peer_reflexive;
15pub mod candidate_relay;
16pub mod candidate_server_reflexive;
17
18use crate::network_type::NetworkType;
19use crate::tcp_type::TcpType;
20use crc::{Crc, CRC_32_ISCSI};
21use serde::Serialize;
22use shared::error::*;
23use std::fmt;
24use std::net::{IpAddr, SocketAddr};
25use std::time::Instant;
26
27use crate::candidate::candidate_host::CandidateHostConfig;
28use crate::candidate::candidate_peer_reflexive::CandidatePeerReflexiveConfig;
29use crate::candidate::candidate_relay::CandidateRelayConfig;
30use crate::candidate::candidate_server_reflexive::CandidateServerReflexiveConfig;
31use crate::network_type::determine_network_type;
32
33pub(crate) const RECEIVE_MTU: usize = 8192;
34pub(crate) const DEFAULT_LOCAL_PREFERENCE: u16 = 65535;
35
36/// Indicates that the candidate is used for RTP.
37pub(crate) const COMPONENT_RTP: u16 = 1;
38/// Indicates that the candidate is used for RTCP.
39pub(crate) const COMPONENT_RTCP: u16 = 0;
40
41/// Represents the type of candidate `CandidateType` enum.
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
43pub enum CandidateType {
44    #[serde(rename = "unspecified")]
45    Unspecified,
46    #[serde(rename = "host")]
47    Host,
48    #[serde(rename = "srflx")]
49    ServerReflexive,
50    #[serde(rename = "prflx")]
51    PeerReflexive,
52    #[serde(rename = "relay")]
53    Relay,
54}
55
56// String makes CandidateType printable
57impl fmt::Display for CandidateType {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        let s = match *self {
60            CandidateType::Host => "host",
61            CandidateType::ServerReflexive => "srflx",
62            CandidateType::PeerReflexive => "prflx",
63            CandidateType::Relay => "relay",
64            CandidateType::Unspecified => "Unknown candidate type",
65        };
66        write!(f, "{s}")
67    }
68}
69
70impl Default for CandidateType {
71    fn default() -> Self {
72        Self::Unspecified
73    }
74}
75
76impl CandidateType {
77    /// Returns the preference weight of a `CandidateType`.
78    ///
79    /// 4.1.2.2.  Guidelines for Choosing Type and Local Preferences
80    /// The RECOMMENDED values are 126 for host candidates, 100
81    /// for server reflexive candidates, 110 for peer reflexive candidates,
82    /// and 0 for relayed candidates.
83    #[must_use]
84    pub const fn preference(self) -> u16 {
85        match self {
86            Self::Host => 126,
87            Self::PeerReflexive => 110,
88            Self::ServerReflexive => 100,
89            Self::Relay | CandidateType::Unspecified => 0,
90        }
91    }
92}
93
94pub(crate) fn contains_candidate_type(
95    candidate_type: CandidateType,
96    candidate_type_list: &[CandidateType],
97) -> bool {
98    if candidate_type_list.is_empty() {
99        return false;
100    }
101    for ct in candidate_type_list {
102        if *ct == candidate_type {
103            return true;
104        }
105    }
106    false
107}
108
109/// Convey transport addresses related to the candidate, useful for diagnostics and other purposes.
110#[derive(PartialEq, Eq, Debug, Clone)]
111pub struct CandidateRelatedAddress {
112    pub address: String,
113    pub port: u16,
114}
115
116// String makes CandidateRelatedAddress printable
117impl fmt::Display for CandidateRelatedAddress {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        write!(f, " related {}:{}", self.address, self.port)
120    }
121}
122
123#[derive(Default)]
124pub struct CandidateConfig {
125    pub candidate_id: String,
126    pub network: String,
127    pub address: String,
128    pub port: u16,
129    pub component: u16,
130    pub priority: u32,
131    pub foundation: String,
132}
133
134#[derive(Clone)]
135pub struct Candidate {
136    pub(crate) id: String,
137    pub(crate) network_type: NetworkType,
138    pub(crate) candidate_type: CandidateType,
139
140    pub(crate) component: u16,
141    pub(crate) address: String,
142    pub(crate) port: u16,
143    pub(crate) related_address: Option<CandidateRelatedAddress>,
144    pub(crate) tcp_type: TcpType,
145
146    pub(crate) resolved_addr: SocketAddr,
147
148    pub(crate) last_sent: Instant,
149    pub(crate) last_received: Instant,
150
151    pub(crate) foundation_override: String,
152    pub(crate) priority_override: u32,
153
154    pub(crate) network: String,
155}
156
157impl Default for Candidate {
158    fn default() -> Self {
159        Self {
160            id: String::new(),
161            network_type: NetworkType::Unspecified,
162            candidate_type: CandidateType::default(),
163
164            component: 0,
165            address: String::new(),
166            port: 0,
167            related_address: None,
168            tcp_type: TcpType::default(),
169
170            resolved_addr: SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 0),
171
172            last_sent: Instant::now(),
173            last_received: Instant::now(),
174
175            foundation_override: String::new(),
176            priority_override: 0,
177            network: String::new(),
178        }
179    }
180}
181
182// String makes the candidateBase printable
183impl fmt::Display for Candidate {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        if let Some(related_address) = self.related_address() {
186            write!(
187                f,
188                "{} {} {}:{}{}",
189                self.network_type(),
190                self.candidate_type(),
191                self.address(),
192                self.port(),
193                related_address,
194            )
195        } else {
196            write!(
197                f,
198                "{} {} {}:{}",
199                self.network_type(),
200                self.candidate_type(),
201                self.address(),
202                self.port(),
203            )
204        }
205    }
206}
207
208impl Candidate {
209    pub fn foundation(&self) -> String {
210        if !self.foundation_override.is_empty() {
211            return self.foundation_override.clone();
212        }
213
214        let mut buf = vec![];
215        buf.extend_from_slice(self.candidate_type().to_string().as_bytes());
216        buf.extend_from_slice(self.address.as_bytes());
217        buf.extend_from_slice(self.network_type().to_string().as_bytes());
218
219        let checksum = Crc::<u32>::new(&CRC_32_ISCSI).checksum(&buf);
220
221        format!("{checksum}")
222    }
223
224    /// Returns Candidate ID.
225    pub fn id(&self) -> String {
226        self.id.clone()
227    }
228
229    /// Returns candidate component.
230    pub fn component(&self) -> u16 {
231        self.component
232    }
233
234    /// Sets candidate component.
235    pub fn set_component(&mut self, component: u16) {
236        self.component = component;
237    }
238
239    /// Returns a time indicating the last time this candidate was received.
240    pub fn last_received(&self) -> Instant {
241        self.last_received
242    }
243
244    /// Returns a time indicating the last time this candidate was sent.
245    pub fn last_sent(&self) -> Instant {
246        self.last_sent
247    }
248
249    /// Returns candidate NetworkType.
250    pub fn network_type(&self) -> NetworkType {
251        self.network_type
252    }
253
254    /// Returns Candidate Address.
255    pub fn address(&self) -> &str {
256        self.address.as_str()
257    }
258
259    /// Returns Candidate Port.
260    pub fn port(&self) -> u16 {
261        self.port
262    }
263
264    /// Computes the priority for this ICE Candidate.
265    pub fn priority(&self) -> u32 {
266        if self.priority_override != 0 {
267            return self.priority_override;
268        }
269
270        // The local preference MUST be an integer from 0 (lowest preference) to
271        // 65535 (highest preference) inclusive.  When there is only a single IP
272        // address, this value SHOULD be set to 65535.  If there are multiple
273        // candidates for a particular component for a particular data stream
274        // that have the same type, the local preference MUST be unique for each
275        // one.
276        (1 << 24) * u32::from(self.candidate_type().preference())
277            + (1 << 8) * u32::from(self.local_preference())
278            + (256 - u32::from(self.component()))
279    }
280
281    /// Returns `Option<CandidateRelatedAddress>`.
282    pub fn related_address(&self) -> Option<CandidateRelatedAddress> {
283        self.related_address.as_ref().cloned()
284    }
285
286    /// Returns candidate type.
287    pub fn candidate_type(&self) -> CandidateType {
288        self.candidate_type
289    }
290
291    pub fn tcp_type(&self) -> TcpType {
292        self.tcp_type
293    }
294
295    /// Returns the string representation of the ICECandidate.
296    pub fn marshal(&self) -> String {
297        let mut val = format!(
298            "{} {} {} {} {} {} typ {}",
299            self.foundation(),
300            self.component(),
301            self.network_type().network_short(),
302            self.priority(),
303            self.address(),
304            self.port(),
305            self.candidate_type()
306        );
307
308        if self.tcp_type != TcpType::Unspecified {
309            val += format!(" tcptype {}", self.tcp_type()).as_str();
310        }
311
312        if let Some(related_address) = self.related_address() {
313            val += format!(
314                " raddr {} rport {}",
315                related_address.address, related_address.port,
316            )
317            .as_str();
318        }
319
320        val
321    }
322
323    pub fn addr(&self) -> SocketAddr {
324        self.resolved_addr
325    }
326
327    pub fn seen(&mut self, outbound: bool) {
328        let now = Instant::now();
329
330        if outbound {
331            self.set_last_sent(now);
332        } else {
333            self.set_last_received(now);
334        }
335    }
336
337    /// Used to compare two candidateBases.
338    pub fn equal(&self, other: &Candidate) -> bool {
339        self.network_type() == other.network_type()
340            && self.candidate_type() == other.candidate_type()
341            && self.address() == other.address()
342            && self.port() == other.port()
343            && self.tcp_type() == other.tcp_type()
344            && self.related_address() == other.related_address()
345    }
346}
347
348impl Candidate {
349    pub fn set_last_received(&mut self, now: Instant) {
350        self.last_received = now;
351    }
352
353    pub fn set_last_sent(&mut self, now: Instant) {
354        self.last_sent = now;
355    }
356
357    /// Returns the local preference for this candidate.
358    pub fn local_preference(&self) -> u16 {
359        if self.network_type().is_tcp() {
360            // RFC 6544, section 4.2
361            //
362            // In Section 4.1.2.1 of [RFC5245], a recommended formula for UDP ICE
363            // candidate prioritization is defined.  For TCP candidates, the same
364            // formula and candidate type preferences SHOULD be used, and the
365            // RECOMMENDED type preferences for the new candidate types defined in
366            // this document (see Section 5) are 105 for NAT-assisted candidates and
367            // 75 for UDP-tunneled candidates.
368            //
369            // (...)
370            //
371            // With TCP candidates, the local preference part of the recommended
372            // priority formula is updated to also include the directionality
373            // (active, passive, or simultaneous-open) of the TCP connection.  The
374            // RECOMMENDED local preference is then defined as:
375            //
376            //     local preference = (2^13) * direction-pref + other-pref
377            //
378            // The direction-pref MUST be between 0 and 7 (both inclusive), with 7
379            // being the most preferred.  The other-pref MUST be between 0 and 8191
380            // (both inclusive), with 8191 being the most preferred.  It is
381            // RECOMMENDED that the host, UDP-tunneled, and relayed TCP candidates
382            // have the direction-pref assigned as follows: 6 for active, 4 for
383            // passive, and 2 for S-O.  For the NAT-assisted and server reflexive
384            // candidates, the RECOMMENDED values are: 6 for S-O, 4 for active, and
385            // 2 for passive.
386            //
387            // (...)
388            //
389            // If any two candidates have the same type-preference and direction-
390            // pref, they MUST have a unique other-pref.  With this specification,
391            // this usually only happens with multi-homed hosts, in which case
392            // other-pref is the preference for the particular IP address from which
393            // the candidate was obtained.  When there is only a single IP address,
394            // this value SHOULD be set to the maximum allowed value (8191).
395            let other_pref: u16 = 8191;
396
397            let direction_pref: u16 = match self.candidate_type() {
398                CandidateType::Host | CandidateType::Relay => match self.tcp_type() {
399                    TcpType::Active => 6,
400                    TcpType::Passive => 4,
401                    TcpType::SimultaneousOpen => 2,
402                    TcpType::Unspecified => 0,
403                },
404                CandidateType::PeerReflexive | CandidateType::ServerReflexive => {
405                    match self.tcp_type() {
406                        TcpType::SimultaneousOpen => 6,
407                        TcpType::Active => 4,
408                        TcpType::Passive => 2,
409                        TcpType::Unspecified => 0,
410                    }
411                }
412                CandidateType::Unspecified => 0,
413            };
414
415            (1 << 13) * direction_pref + other_pref
416        } else {
417            DEFAULT_LOCAL_PREFERENCE
418        }
419    }
420}
421
422/// Creates a Candidate from its string representation.
423pub fn unmarshal_candidate(raw: &str) -> Result<Candidate> {
424    let split: Vec<&str> = raw.split_whitespace().collect();
425    if split.len() < 8 {
426        return Err(Error::Other(format!(
427            "{:?} ({})",
428            Error::ErrAttributeTooShortIceCandidate,
429            split.len()
430        )));
431    }
432
433    // Foundation
434    let foundation = split[0].to_owned();
435
436    // Component
437    let component: u16 = split[1].parse()?;
438
439    // Network
440    let network = split[2].to_owned();
441
442    // Priority
443    let priority: u32 = split[3].parse()?;
444
445    // Address
446    let address = split[4].to_owned();
447
448    // Port
449    let port: u16 = split[5].parse()?;
450
451    let typ = split[7];
452
453    let mut rel_addr = String::new();
454    let mut rel_port = 0;
455    let mut tcp_type = TcpType::Unspecified;
456
457    if split.len() > 8 {
458        let split2 = &split[8..];
459
460        if split2[0] == "raddr" {
461            if split2.len() < 4 {
462                return Err(Error::Other(format!(
463                    "{:?}: incorrect length",
464                    Error::ErrParseRelatedAddr
465                )));
466            }
467
468            // RelatedAddress
469            rel_addr = split2[1].to_owned();
470
471            // RelatedPort
472            rel_port = split2[3].parse()?;
473        } else if split2[0] == "tcptype" {
474            if split2.len() < 2 {
475                return Err(Error::Other(format!(
476                    "{:?}: incorrect length",
477                    Error::ErrParseType
478                )));
479            }
480
481            tcp_type = TcpType::from(split2[1]);
482        }
483    }
484
485    match typ {
486        "host" => {
487            let config = CandidateHostConfig {
488                base_config: CandidateConfig {
489                    network,
490                    address,
491                    port,
492                    component,
493                    priority,
494                    foundation,
495                    ..CandidateConfig::default()
496                },
497                tcp_type,
498            };
499            config.new_candidate_host()
500        }
501        "srflx" => {
502            let config = CandidateServerReflexiveConfig {
503                base_config: CandidateConfig {
504                    network,
505                    address,
506                    port,
507                    component,
508                    priority,
509                    foundation,
510                    ..CandidateConfig::default()
511                },
512                rel_addr,
513                rel_port,
514            };
515            config.new_candidate_server_reflexive()
516        }
517        "prflx" => {
518            let config = CandidatePeerReflexiveConfig {
519                base_config: CandidateConfig {
520                    network,
521                    address,
522                    port,
523                    component,
524                    priority,
525                    foundation,
526                    ..CandidateConfig::default()
527                },
528                rel_addr,
529                rel_port,
530            };
531
532            config.new_candidate_peer_reflexive()
533        }
534        "relay" => {
535            let config = CandidateRelayConfig {
536                base_config: CandidateConfig {
537                    network,
538                    address,
539                    port,
540                    component,
541                    priority,
542                    foundation,
543                    ..CandidateConfig::default()
544                },
545                rel_addr,
546                rel_port,
547            };
548            config.new_candidate_relay()
549        }
550        _ => Err(Error::Other(format!(
551            "{:?} ({})",
552            Error::ErrUnknownCandidateType,
553            typ
554        ))),
555    }
556}