msf_ice/
candidate.rs

1use std::{
2    error::Error,
3    fmt::{self, Display, Formatter},
4    net::SocketAddr,
5};
6
7use crate::AgentRole;
8
9/// Candidate type.
10#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
11pub enum CandidateKind {
12    Host,
13    ServerReflexive,
14    PeerReflexive,
15    Relayed,
16}
17
18/// Local candidate.
19#[derive(Copy, Clone)]
20pub struct LocalCandidate {
21    channel: usize,
22    component: u8,
23    kind: CandidateKind,
24    base: SocketAddr,
25    addr: SocketAddr,
26    foundation: u32,
27    priority: u32,
28}
29
30impl LocalCandidate {
31    /// Calculate local candidate priority for a given component ID, candidate
32    /// type and the candidate base address.
33    #[inline]
34    pub const fn calculate_priority(component: u8, kind: CandidateKind, base: SocketAddr) -> u32 {
35        let type_preference = match kind {
36            CandidateKind::Host => 126,
37            CandidateKind::PeerReflexive => 110,
38            CandidateKind::ServerReflexive => 100,
39            CandidateKind::Relayed => 0,
40        };
41
42        let local_preference = if matches!(base, SocketAddr::V6(_)) {
43            65_535
44        } else {
45            65_534
46        };
47
48        type_preference << 24 | local_preference << 8 | (255 - component as u32)
49    }
50
51    /// Create a new host candidate.
52    #[inline]
53    pub const fn host(channel: usize, component: u8, addr: SocketAddr) -> Self {
54        Self {
55            channel,
56            component,
57            kind: CandidateKind::Host,
58            base: addr,
59            addr,
60            foundation: 0,
61            priority: Self::calculate_priority(component, CandidateKind::Host, addr),
62        }
63    }
64
65    /// Create a new server-reflexive candidate.
66    #[inline]
67    pub const fn server_reflexive(
68        channel: usize,
69        component: u8,
70        base: SocketAddr,
71        addr: SocketAddr,
72    ) -> Self {
73        Self {
74            channel,
75            component,
76            kind: CandidateKind::ServerReflexive,
77            base,
78            addr,
79            foundation: 0,
80            priority: Self::calculate_priority(component, CandidateKind::ServerReflexive, base),
81        }
82    }
83
84    /// Create a new peer-reflexive candidate.
85    #[inline]
86    pub const fn peer_reflexive(
87        channel: usize,
88        component: u8,
89        base: SocketAddr,
90        addr: SocketAddr,
91    ) -> Self {
92        Self {
93            channel,
94            component,
95            kind: CandidateKind::PeerReflexive,
96            base,
97            addr,
98            foundation: 0,
99            priority: Self::calculate_priority(component, CandidateKind::PeerReflexive, base),
100        }
101    }
102
103    /// Create a new relayed candidate.
104    #[inline]
105    pub const fn relayed(channel: usize, component: u8, addr: SocketAddr) -> Self {
106        // NOTE: addr and base are the same for relayed candidates because we
107        // do not use a specific local transport address to deliver data to the
108        // corresponding TURN server (i.e. the local address can be arbitrary)
109        Self {
110            channel,
111            component,
112            kind: CandidateKind::Relayed,
113            base: addr,
114            addr,
115            foundation: 0,
116            priority: Self::calculate_priority(component, CandidateKind::Relayed, addr),
117        }
118    }
119
120    /// Create a new candidate with a given foundation value.
121    #[inline]
122    pub fn with_foundation(mut self, foundation: u32) -> Self {
123        self.foundation = foundation;
124        self
125    }
126
127    /// Get index of the (media) channel this candidate belongs to.
128    #[inline]
129    pub fn channel(&self) -> usize {
130        self.channel
131    }
132
133    /// Get component ID of the component this candidate belongs to.
134    ///
135    /// # Note
136    /// Unlike the component ID definition in RFC 5245, this component ID is
137    /// zero-based. In order to get the RFC 5245 component ID (e.g. to create
138    /// a session description), you need to add one to this number.
139    #[inline]
140    pub fn component(&self) -> u8 {
141        self.component
142    }
143
144    /// Get type of the candidate.
145    #[inline]
146    pub fn kind(&self) -> CandidateKind {
147        self.kind
148    }
149
150    /// Get the base address.
151    #[inline]
152    pub fn base(&self) -> SocketAddr {
153        self.base
154    }
155
156    /// Get the address.
157    #[inline]
158    pub fn addr(&self) -> SocketAddr {
159        self.addr
160    }
161
162    /// Get candidate priority.
163    #[inline]
164    pub fn priority(&self) -> u32 {
165        self.priority
166    }
167
168    /// Get the foundation.
169    #[inline]
170    pub fn foundation(&self) -> u32 {
171        self.foundation
172    }
173}
174
175/// Remote candidate.
176#[derive(Clone)]
177pub struct RemoteCandidate {
178    channel: usize,
179    component: u8,
180    kind: CandidateKind,
181    addr: SocketAddr,
182    foundation: String,
183    priority: u32,
184}
185
186impl RemoteCandidate {
187    /// Create a new remote candidate.
188    ///
189    /// # Note
190    /// Unlike the component ID definition in RFC 5245, this component ID is
191    /// zero-based. If you're creating a new remote candidate from an RFC 5245
192    /// component ID (e.g. from a session description), make sure to subtract
193    /// one.
194    #[inline]
195    pub fn new<T>(
196        channel: usize,
197        component: u8,
198        kind: CandidateKind,
199        addr: SocketAddr,
200        foundation: T,
201        priority: u32,
202    ) -> Self
203    where
204        T: ToString,
205    {
206        Self {
207            channel,
208            component,
209            kind,
210            addr,
211            foundation: foundation.to_string(),
212            priority,
213        }
214    }
215
216    /// Create a new peer-reflexive remote candidate.
217    #[inline]
218    pub fn peer_reflexive(channel: usize, component: u8, addr: SocketAddr, priority: u32) -> Self {
219        Self {
220            channel,
221            component,
222            kind: CandidateKind::PeerReflexive,
223            addr,
224            foundation: addr.to_string(),
225            priority,
226        }
227    }
228
229    /// Get index of the (media) channel this candidate belongs to.
230    #[inline]
231    pub fn channel(&self) -> usize {
232        self.channel
233    }
234
235    /// Get component ID of the component this candidate belongs to.
236    #[inline]
237    pub fn component(&self) -> u8 {
238        self.component
239    }
240
241    /// Get type of the candidate.
242    #[inline]
243    pub fn kind(&self) -> CandidateKind {
244        self.kind
245    }
246
247    /// Get the address.
248    #[inline]
249    pub fn addr(&self) -> SocketAddr {
250        self.addr
251    }
252
253    /// Get candidate priority.
254    #[inline]
255    pub fn priority(&self) -> u32 {
256        self.priority
257    }
258
259    /// Get the foundation.
260    #[inline]
261    pub fn foundation(&self) -> &str {
262        &self.foundation
263    }
264}
265
266/// Local-remote candidate pair.
267#[derive(Clone)]
268pub struct CandidatePair {
269    local: LocalCandidate,
270    remote: RemoteCandidate,
271    foundation: String,
272}
273
274impl CandidatePair {
275    /// Create a new candidate pair.
276    pub fn new(
277        local: LocalCandidate,
278        remote: RemoteCandidate,
279    ) -> Result<Self, InvalidCandidatePair> {
280        if local.channel != remote.channel || local.component != remote.component {
281            return Err(InvalidCandidatePair);
282        }
283
284        let local_addr = local.addr();
285
286        match remote.addr() {
287            SocketAddr::V4(_) if local_addr.is_ipv4() => (),
288            SocketAddr::V6(_) if local_addr.is_ipv6() => (),
289            _ => return Err(InvalidCandidatePair),
290        }
291
292        let foundation = format!("{}:{}", local.foundation(), remote.foundation());
293
294        let res = Self {
295            local,
296            remote,
297            foundation,
298        };
299
300        Ok(res)
301    }
302
303    /// Get the local candidate.
304    pub fn local(&self) -> &LocalCandidate {
305        &self.local
306    }
307
308    /// Get the remote candidate.
309    pub fn remote(&self) -> &RemoteCandidate {
310        &self.remote
311    }
312
313    /// Get the pair priority.
314    pub fn priority(&self, local_role: AgentRole) -> u64 {
315        let (g, d) = match local_role {
316            AgentRole::Controlling => (self.local.priority(), self.remote.priority()),
317            AgentRole::Controlled => (self.remote.priority(), self.local.priority()),
318        };
319
320        let min = g.min(d) as u64;
321        let max = g.max(d) as u64;
322
323        (min << 32) + (max << 1) + u64::from(g > d)
324    }
325
326    /// Get the component ID.
327    pub fn component(&self) -> u8 {
328        self.remote.component()
329    }
330
331    /// Get foundation of the pair.
332    pub fn foundation(&self) -> &str {
333        &self.foundation
334    }
335}
336
337/// Invalid candidate pair.
338#[derive(Debug, Copy, Clone)]
339pub struct InvalidCandidatePair;
340
341impl Display for InvalidCandidatePair {
342    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
343        f.write_str("the given local and remote candidates do not form a pair")
344    }
345}
346
347impl Error for InvalidCandidatePair {}