Skip to main content

rtc_ice/agent/
mod.rs

1//! The Sans-I/O ICE agent.
2//!
3//! An [`Agent`](crate::agent::Agent) is given local and remote candidates and inbound datagrams; it produces the
4//! connectivity checks to send, the state transitions to report, and eventually a selected
5//! candidate pair. It owns no sockets and no clock — the caller drives time with
6//! `handle_timeout`.
7//!
8//! Two roles exist: the controlling agent nominates the pair that will carry media, the
9//! controlled agent accepts that choice. Which side controls is decided by which offered, and the
10//! two must not agree — see [`Credentials`](crate::agent::Credentials) and [`Agent::set_role`](crate::agent::Agent::set_role).
11#[cfg(test)]
12mod agent_test;
13
14/// Configuration for a new [`Agent`]: servers, timeouts and role.
15pub mod agent_config;
16mod agent_proto;
17/// Pair selection and nomination — which candidate pair becomes the selected one.
18pub mod agent_selector;
19/// Snapshot statistics for an agent's candidates and pairs.
20pub mod agent_stats;
21
22use agent_config::*;
23use bytes::BytesMut;
24use log::{debug, error, info, trace, warn};
25use mdns::{Mdns, QueryId};
26use sansio::Protocol;
27use std::collections::{HashMap, VecDeque};
28use std::net::{IpAddr, Ipv4Addr, SocketAddr};
29use std::sync::Arc;
30use std::time::{Duration, Instant};
31use stun::attributes::*;
32use stun::fingerprint::*;
33use stun::integrity::*;
34use stun::message::*;
35use stun::textattrs::*;
36use stun::xoraddr::*;
37
38use crate::candidate::candidate_peer_reflexive::CandidatePeerReflexiveConfig;
39use crate::candidate::{candidate_pair::*, *};
40use crate::mdns::{MulticastDnsMode, create_multicast_dns, generate_multicast_dns_name};
41use crate::network_type::NetworkType;
42use crate::rand::*;
43use crate::state::*;
44use crate::tcp_type::TcpType;
45use crate::url::*;
46use shared::error::*;
47use shared::{TaggedBytesMut, TransportContext, TransportProtocol};
48
49const ZERO_DURATION: Duration = Duration::from_secs(0);
50
51#[derive(Debug, Clone)]
52pub(crate) struct BindingRequest {
53    pub(crate) timestamp: Instant,
54    pub(crate) transaction_id: TransactionId,
55    pub(crate) destination: SocketAddr,
56    pub(crate) is_use_candidate: bool,
57}
58
59impl Default for BindingRequest {
60    fn default() -> Self {
61        Self {
62            timestamp: Instant::now(),
63            transaction_id: TransactionId::default(),
64            destination: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 0),
65            is_use_candidate: false,
66        }
67    }
68}
69
70#[derive(Default, Clone)]
71/// The ICE credentials for one side of a session, exchanged in SDP.
72pub struct Credentials {
73    /// The username fragment, echoed in every STUN check so a receiver can demultiplex.
74    pub ufrag: String,
75    /// The password, used as the `MESSAGE-INTEGRITY` key.
76    pub pwd: String,
77}
78
79#[derive(Default, Clone)]
80pub(crate) struct UfragPwd {
81    pub(crate) local_credentials: Credentials,
82    pub(crate) remote_credentials: Option<Credentials>,
83}
84
85fn assert_inbound_username(m: &Message, expected_username: &str) -> Result<()> {
86    let mut username = Username::new(ATTR_USERNAME, String::new());
87    username.get_from(m)?;
88
89    if username.to_string() != expected_username {
90        return Err(Error::Other(format!(
91            "{:?} expected({}) actual({})",
92            Error::ErrMismatchUsername,
93            expected_username,
94            username,
95        )));
96    }
97
98    Ok(())
99}
100
101fn assert_inbound_message_integrity(m: &mut Message, key: &[u8]) -> Result<()> {
102    let message_integrity_attr = MessageIntegrity(key.to_vec());
103    message_integrity_attr.check(m)
104}
105
106/// What the agent reports to its caller.
107pub enum Event {
108    /// The agent's connection state changed.
109    ConnectionStateChange(ConnectionState),
110    /// A new pair was selected, carrying the local and remote candidates.
111    ///
112    /// Media should be sent on this pair from now on.
113    SelectedCandidatePairChange(Box<Candidate>, Box<Candidate>),
114    /// Emitted when the ICE role switches due to a role conflict (RFC 8445 §7.3.1.1).
115    /// The bool is `true` if the agent is now controlling, `false` if now controlled.
116    RoleChange(bool),
117}
118
119/// Represents the ICE agent.
120pub struct Agent {
121    pub(crate) tie_breaker: u64,
122    pub(crate) is_controlling: bool,
123    pub(crate) lite: bool,
124
125    pub(crate) start_time: Instant,
126
127    pub(crate) connection_state: ConnectionState,
128    pub(crate) last_connection_state: ConnectionState,
129
130    //pub(crate) started_ch_tx: Mutex<Option<broadcast::Sender<()>>>,
131    pub(crate) ufrag_pwd: UfragPwd,
132
133    pub(crate) local_candidates: Vec<Candidate>,
134    pub(crate) remote_candidates: Vec<Candidate>,
135    pub(crate) candidate_pairs: Vec<CandidatePair>,
136    pub(crate) nominated_pair: Option<usize>,
137    pub(crate) selected_pair: Option<usize>,
138
139    // LRU of outbound Binding request Transaction IDs
140    pub(crate) pending_binding_requests: Vec<BindingRequest>,
141
142    // the following variables won't be changed after init_with_defaults()
143    pub(crate) insecure_skip_verify: bool,
144    pub(crate) max_binding_requests: u16,
145    pub(crate) host_acceptance_min_wait: Duration,
146    pub(crate) srflx_acceptance_min_wait: Duration,
147    pub(crate) prflx_acceptance_min_wait: Duration,
148    pub(crate) relay_acceptance_min_wait: Duration,
149    // How long connectivity checks can fail before the ICE Agent
150    // goes to disconnected
151    pub(crate) disconnected_timeout: Duration,
152    // How long connectivity checks can fail before the ICE Agent
153    // goes to failed
154    pub(crate) failed_timeout: Duration,
155    // How often should we send keepalive packets?
156    // 0 means never
157    pub(crate) keepalive_interval: Duration,
158    // When the last STUN consent ping was sent.
159    pub(crate) last_consent_sent: Instant,
160    // How often should we run our internal taskLoop to check for state changes when connecting
161    pub(crate) check_interval: Duration,
162    pub(crate) checking_duration: Instant,
163    pub(crate) last_checking_time: Instant,
164    // When set, a connectivity check has been requested and must be run on the next
165    // `handle_timeout`. Checks are deferred (rather than run inline) so that adding a
166    // candidate can never re-enter `contact()` and mutate agent state - e.g. transition
167    // to `Failed` and drop candidates - while a caller still holds indices into the
168    // candidate vectors. See issue #88.
169    pub(crate) force_candidate_contact: bool,
170
171    pub(crate) mdns: Option<Mdns>,
172    pub(crate) mdns_queries: HashMap<QueryId, Candidate>,
173
174    pub(crate) mdns_mode: MulticastDnsMode,
175    pub(crate) mdns_local_name: String,
176    pub(crate) mdns_local_ip: Option<IpAddr>,
177
178    pub(crate) candidate_types: Vec<CandidateType>,
179    pub(crate) network_types: Vec<NetworkType>,
180    pub(crate) urls: Vec<Url>,
181
182    pub(crate) write_outs: VecDeque<TaggedBytesMut>,
183    pub(crate) event_outs: VecDeque<Event>,
184}
185
186impl Default for Agent {
187    fn default() -> Self {
188        Self {
189            tie_breaker: 0,
190            is_controlling: false,
191            lite: false,
192            start_time: Instant::now(),
193            connection_state: Default::default(),
194            last_connection_state: Default::default(),
195            ufrag_pwd: Default::default(),
196            local_candidates: vec![],
197            remote_candidates: vec![],
198            candidate_pairs: vec![],
199            nominated_pair: None,
200            selected_pair: None,
201            pending_binding_requests: vec![],
202            insecure_skip_verify: false,
203            max_binding_requests: 0,
204            host_acceptance_min_wait: Default::default(),
205            srflx_acceptance_min_wait: Default::default(),
206            prflx_acceptance_min_wait: Default::default(),
207            relay_acceptance_min_wait: Default::default(),
208            disconnected_timeout: Default::default(),
209            failed_timeout: Default::default(),
210            keepalive_interval: Default::default(),
211            last_consent_sent: Instant::now(),
212            check_interval: Default::default(),
213            checking_duration: Instant::now(),
214            last_checking_time: Instant::now(),
215            force_candidate_contact: false,
216            mdns_mode: MulticastDnsMode::Disabled,
217            mdns_local_name: "".to_owned(),
218            mdns_local_ip: None,
219            mdns_queries: HashMap::new(),
220            mdns: None,
221            candidate_types: vec![],
222            network_types: vec![],
223            urls: vec![],
224            write_outs: Default::default(),
225            event_outs: Default::default(),
226        }
227    }
228}
229
230impl Agent {
231    /// Creates a new Agent.
232    pub fn new(config: Arc<AgentConfig>) -> Result<Self> {
233        let mut mdns_local_name = config.multicast_dns_local_name.clone();
234        if mdns_local_name.is_empty() {
235            mdns_local_name = generate_multicast_dns_name();
236        }
237
238        if !mdns_local_name.ends_with(".local") || mdns_local_name.split('.').count() != 2 {
239            return Err(Error::ErrInvalidMulticastDnshostName);
240        }
241
242        let mdns_mode = config.multicast_dns_mode;
243        let mdns = create_multicast_dns(
244            mdns_mode,
245            &mdns_local_name,
246            &config.multicast_dns_local_ip,
247            &config.multicast_dns_query_timeout,
248        )
249        .unwrap_or_else(|err| {
250            // Opportunistic mDNS: If we can't open the connection, that's ok: we
251            // can continue without it.
252            warn!("Failed to initialize mDNS {mdns_local_name}: {err}");
253            None
254        });
255
256        let candidate_types = if config.candidate_types.is_empty() {
257            default_candidate_types()
258        } else {
259            config.candidate_types.clone()
260        };
261
262        if config.lite && (candidate_types.len() != 1 || candidate_types[0] != CandidateType::Host)
263        {
264            return Err(Error::ErrLiteUsingNonHostCandidates);
265        }
266
267        if !config.urls.is_empty()
268            && !contains_candidate_type(CandidateType::ServerReflexive, &candidate_types)
269            && !contains_candidate_type(CandidateType::Relay, &candidate_types)
270        {
271            return Err(Error::ErrUselessUrlsProvided);
272        }
273
274        let mut agent = Self {
275            tie_breaker: rand::random::<u64>(),
276            is_controlling: config.is_controlling,
277            lite: config.lite,
278
279            start_time: Instant::now(),
280
281            nominated_pair: None,
282            selected_pair: None,
283            candidate_pairs: vec![],
284
285            connection_state: ConnectionState::New,
286
287            insecure_skip_verify: config.insecure_skip_verify,
288
289            //started_ch_tx: MuteSome(started_ch_tx)),
290
291            //won't change after init_with_defaults()
292            max_binding_requests: if let Some(max_binding_requests) = config.max_binding_requests {
293                max_binding_requests
294            } else {
295                DEFAULT_MAX_BINDING_REQUESTS
296            },
297            host_acceptance_min_wait: if let Some(host_acceptance_min_wait) =
298                config.host_acceptance_min_wait
299            {
300                host_acceptance_min_wait
301            } else {
302                DEFAULT_HOST_ACCEPTANCE_MIN_WAIT
303            },
304            srflx_acceptance_min_wait: if let Some(srflx_acceptance_min_wait) =
305                config.srflx_acceptance_min_wait
306            {
307                srflx_acceptance_min_wait
308            } else {
309                DEFAULT_SRFLX_ACCEPTANCE_MIN_WAIT
310            },
311            prflx_acceptance_min_wait: if let Some(prflx_acceptance_min_wait) =
312                config.prflx_acceptance_min_wait
313            {
314                prflx_acceptance_min_wait
315            } else {
316                DEFAULT_PRFLX_ACCEPTANCE_MIN_WAIT
317            },
318            relay_acceptance_min_wait: if let Some(relay_acceptance_min_wait) =
319                config.relay_acceptance_min_wait
320            {
321                relay_acceptance_min_wait
322            } else {
323                DEFAULT_RELAY_ACCEPTANCE_MIN_WAIT
324            },
325
326            // How long connectivity checks can fail before the ICE Agent
327            // goes to disconnected
328            disconnected_timeout: if let Some(disconnected_timeout) = config.disconnected_timeout {
329                disconnected_timeout
330            } else {
331                DEFAULT_DISCONNECTED_TIMEOUT
332            },
333
334            // How long connectivity checks can fail before the ICE Agent
335            // goes to failed
336            failed_timeout: if let Some(failed_timeout) = config.failed_timeout {
337                failed_timeout
338            } else {
339                DEFAULT_FAILED_TIMEOUT
340            },
341
342            // How often should we send keepalive packets?
343            // 0 means never
344            keepalive_interval: if let Some(keepalive_interval) = config.keepalive_interval {
345                keepalive_interval
346            } else {
347                DEFAULT_KEEPALIVE_INTERVAL
348            },
349
350            // How often should we run our internal taskLoop to check for state changes when connecting
351            check_interval: if config.check_interval == Duration::from_secs(0) {
352                DEFAULT_CHECK_INTERVAL
353            } else {
354                config.check_interval
355            },
356            last_consent_sent: Instant::now(),
357            checking_duration: Instant::now(),
358            last_checking_time: Instant::now(),
359            force_candidate_contact: false,
360            last_connection_state: ConnectionState::Unspecified,
361
362            mdns,
363            mdns_queries: HashMap::new(),
364
365            mdns_mode,
366            mdns_local_name,
367            mdns_local_ip: config.multicast_dns_local_ip,
368
369            ufrag_pwd: UfragPwd::default(),
370
371            local_candidates: vec![],
372            remote_candidates: vec![],
373
374            // LRU of outbound Binding request Transaction IDs
375            pending_binding_requests: vec![],
376
377            candidate_types,
378            network_types: config.network_types.clone(),
379            urls: config.urls.clone(),
380
381            write_outs: VecDeque::new(),
382            event_outs: VecDeque::new(),
383        };
384
385        // Restart is also used to initialize the agent for the first time
386        if let Err(err) = agent.restart(config.local_ufrag.clone(), config.local_pwd.clone(), false)
387        {
388            let _ = agent.close();
389            return Err(err);
390        }
391
392        Ok(agent)
393    }
394
395    /// Adds a new local candidate.
396    pub fn add_local_candidate(&mut self, mut c: Candidate) -> Result<bool> {
397        // Filter by network type if network_types is configured
398        if !self.network_types.is_empty() {
399            let candidate_network_type = c.network_type();
400            if !self.network_types.contains(&candidate_network_type) {
401                debug!(
402                    "Ignoring local candidate with network type {:?} (not in configured network types: {:?})",
403                    candidate_network_type, self.network_types
404                );
405                return Ok(false);
406            }
407        }
408
409        // Filter by candidate type if candidate_types is configured.
410        let candidate_type = c.candidate_type();
411        if !self.candidate_types.is_empty() && !self.candidate_types.contains(&candidate_type) {
412            debug!(
413                "Ignoring local candidate with type {:?} (not in configured candidate types: {:?})",
414                candidate_type, self.candidate_types
415            );
416            return Ok(false);
417        }
418
419        if c.candidate_type() == CandidateType::Host
420            && self.mdns_mode == MulticastDnsMode::QueryAndGather
421            && c.network_type == NetworkType::Udp4
422            && self
423                .mdns_local_ip
424                .is_some_and(|local_ip| local_ip == c.addr().ip())
425        {
426            // only one .local mDNS host candidate per IPv4 is supported
427            // when registered local ip matches, use mdns_local_name to hide local host ip
428            trace!(
429                "mDNS hides local ip {} with local name {}",
430                c.address, self.mdns_local_name
431            );
432            c.address = self.mdns_local_name.clone();
433        }
434
435        for cand in &self.local_candidates {
436            if cand.equal(&c) {
437                return Ok(false);
438            }
439        }
440
441        self.local_candidates.push(c);
442        let local_index = self.local_candidates.len() - 1;
443
444        for remote_index in 0..self.remote_candidates.len() {
445            if self.local_candidates[local_index]
446                .can_pair_with(&self.remote_candidates[remote_index])
447            {
448                self.add_pair(local_index, remote_index);
449            }
450        }
451
452        self.request_connectivity_check();
453
454        Ok(true)
455    }
456
457    /// Adds a new remote candidate.
458    pub fn add_remote_candidate(&mut self, c: Candidate) -> Result<bool> {
459        // Filter by network type if network_types is configured
460        if !self.network_types.is_empty() {
461            let candidate_network_type = c.network_type();
462            if !self.network_types.contains(&candidate_network_type) {
463                debug!(
464                    "Ignoring remote candidate with network type {:?} (not in configured network types: {:?})",
465                    candidate_network_type, self.network_types
466                );
467                return Ok(false);
468            }
469        }
470
471        // TCP active candidates don't have a listening port - they initiate connections.
472        // The remote active side will probe our passive candidates, so we don't need
473        // to do anything with remote active candidates.
474        if c.tcp_type() == TcpType::Active {
475            debug!(
476                "Ignoring remote candidate with tcptype active: {}",
477                c.address()
478            );
479            return Ok(false);
480        }
481
482        // If we have a mDNS Candidate lets fully resolve it before adding it locally
483        if c.candidate_type() == CandidateType::Host && c.address().ends_with(".local") {
484            if self.mdns_mode == MulticastDnsMode::Disabled {
485                warn!(
486                    "remote mDNS candidate added, but mDNS is disabled: ({})",
487                    c.address()
488                );
489                return Ok(false);
490            }
491
492            if c.candidate_type() != CandidateType::Host {
493                return Err(Error::ErrAddressParseFailed);
494            }
495
496            if let Some(mdns_conn) = &mut self.mdns {
497                let query_id = mdns_conn.query(c.address());
498                self.mdns_queries.insert(query_id, c);
499            }
500
501            return Ok(false);
502        }
503
504        self.trigger_request_connectivity_check(vec![c]);
505        Ok(true)
506    }
507
508    fn trigger_request_connectivity_check(&mut self, remote_candidates: Vec<Candidate>) {
509        for c in remote_candidates {
510            if !self.remote_candidates.iter().any(|cand| cand.equal(&c)) {
511                self.remote_candidates.push(c);
512                let remote_index = self.remote_candidates.len() - 1;
513
514                for local_index in 0..self.local_candidates.len() {
515                    if self.local_candidates[local_index]
516                        .can_pair_with(&self.remote_candidates[remote_index])
517                    {
518                        self.add_pair(local_index, remote_index);
519                    }
520                }
521
522                self.request_connectivity_check();
523            }
524        }
525    }
526
527    /// Sets the credentials of the remote agent.
528    pub fn set_remote_credentials(
529        &mut self,
530        remote_ufrag: String,
531        remote_pwd: String,
532    ) -> Result<()> {
533        if remote_ufrag.is_empty() {
534            return Err(Error::ErrRemoteUfragEmpty);
535        } else if remote_pwd.is_empty() {
536            return Err(Error::ErrRemotePwdEmpty);
537        }
538
539        self.ufrag_pwd.remote_credentials = Some(Credentials {
540            ufrag: remote_ufrag,
541            pwd: remote_pwd,
542        });
543
544        Ok(())
545    }
546
547    /// Returns the remote credentials.
548    pub fn get_remote_credentials(&self) -> Option<&Credentials> {
549        self.ufrag_pwd.remote_credentials.as_ref()
550    }
551
552    /// Returns the local credentials.
553    pub fn get_local_credentials(&self) -> &Credentials {
554        &self.ufrag_pwd.local_credentials
555    }
556
557    /// Whether this agent is the controlling one, which decides nomination.
558    pub fn role(&self) -> bool {
559        self.is_controlling
560    }
561
562    /// Sets the controlling role.
563    ///
564    /// Determined by which side offered; the two agents must not agree, or nomination stalls.
565    pub fn set_role(&mut self, is_controlling: bool) {
566        self.is_controlling = is_controlling;
567    }
568
569    /// The agent's current connection state.
570    pub fn state(&self) -> ConnectionState {
571        self.connection_state
572    }
573
574    /// Whether a non-STUN datagram on `transport` should be accepted as media.
575    ///
576    /// Guards against accepting media from an address that has not passed a connectivity check.
577    pub fn is_valid_non_stun_traffic(&mut self, transport: TransportContext) -> bool {
578        self.find_local_candidate(transport.local_addr, transport.transport_protocol)
579            .is_some()
580            && self.validate_non_stun_traffic(transport.peer_addr)
581    }
582
583    fn get_timeout_interval(&self) -> Duration {
584        let (check_interval, keepalive_interval, disconnected_timeout, failed_timeout) = (
585            self.check_interval,
586            self.keepalive_interval,
587            self.disconnected_timeout,
588            self.failed_timeout,
589        );
590        let mut interval = DEFAULT_CHECK_INTERVAL;
591
592        let mut update_interval = |x: Duration| {
593            if x != ZERO_DURATION && (interval == ZERO_DURATION || interval > x) {
594                interval = x;
595            }
596        };
597
598        match self.last_connection_state {
599            ConnectionState::New | ConnectionState::Checking => {
600                // While connecting, check candidates more frequently
601                update_interval(check_interval);
602            }
603            ConnectionState::Connected | ConnectionState::Disconnected => {
604                update_interval(keepalive_interval);
605            }
606            _ => {}
607        };
608        // Ensure we run our task loop as quickly as the minimum of our various configured timeouts
609        update_interval(disconnected_timeout);
610        update_interval(failed_timeout);
611        interval
612    }
613
614    /// Returns the selected pair (local_candidate, remote_candidate) or none
615    pub fn get_selected_candidate_pair(&self) -> Option<(&Candidate, &Candidate)> {
616        if let Some(pair_index) = self.get_selected_pair() {
617            let candidate_pair = &self.candidate_pairs[pair_index];
618            Some((
619                &self.local_candidates[candidate_pair.local_index],
620                &self.remote_candidates[candidate_pair.remote_index],
621            ))
622        } else {
623            None
624        }
625    }
626
627    /// The highest-priority pair that is still usable, whether or not it has been nominated.
628    pub fn get_best_available_candidate_pair(&self) -> Option<(&Candidate, &Candidate)> {
629        if let Some(pair_index) = self.get_best_available_pair() {
630            let candidate_pair = &self.candidate_pairs[pair_index];
631            Some((
632                &self.local_candidates[candidate_pair.local_index],
633                &self.remote_candidates[candidate_pair.remote_index],
634            ))
635        } else {
636            None
637        }
638    }
639
640    /// start connectivity checks
641    pub fn start_connectivity_checks(
642        &mut self,
643        is_controlling: bool,
644        remote_ufrag: String,
645        remote_pwd: String,
646    ) -> Result<()> {
647        debug!(
648            "Started agent: isControlling? {}, remoteUfrag: {}, remotePwd: {}",
649            is_controlling, remote_ufrag, remote_pwd
650        );
651        self.set_remote_credentials(remote_ufrag, remote_pwd)?;
652        self.is_controlling = is_controlling;
653        self.start();
654
655        self.update_connection_state(ConnectionState::Checking);
656        self.request_connectivity_check();
657
658        Ok(())
659    }
660
661    /// Restarts the ICE Agent with the provided ufrag/pwd
662    /// If no ufrag/pwd is provided the Agent will generate one itself.
663    pub fn restart(
664        &mut self,
665        mut ufrag: String,
666        mut pwd: String,
667        keep_local_candidates: bool,
668    ) -> Result<()> {
669        if ufrag.is_empty() {
670            ufrag = generate_ufrag();
671        }
672        if pwd.is_empty() {
673            pwd = generate_pwd();
674        }
675
676        if ufrag.len() * 8 < 24 {
677            return Err(Error::ErrLocalUfragInsufficientBits);
678        }
679        if pwd.len() * 8 < 128 {
680            return Err(Error::ErrLocalPwdInsufficientBits);
681        }
682
683        // Clear all agent needed to take back to fresh state
684        self.ufrag_pwd.local_credentials.ufrag = ufrag;
685        self.ufrag_pwd.local_credentials.pwd = pwd;
686        self.ufrag_pwd.remote_credentials = None;
687
688        self.pending_binding_requests = vec![];
689
690        self.candidate_pairs = vec![];
691
692        self.set_selected_pair(None);
693        self.delete_all_candidates(keep_local_candidates);
694        self.start();
695
696        // Restart is used by NewAgent. Accept/Connect should be used to move to checking
697        // for new Agents
698        if self.connection_state != ConnectionState::New {
699            self.update_connection_state(ConnectionState::Checking);
700        }
701
702        Ok(())
703    }
704
705    /// Returns the local candidates.
706    pub fn get_local_candidates(&self) -> &[Candidate] {
707        &self.local_candidates
708    }
709
710    fn contact(&mut self, now: Instant) {
711        // Consume any pending deferred-check request now that we are running one.
712        // Reset before the early returns below so a failed/settled agent does not
713        // keep asking `poll_timeout` for an immediate wake-up.
714        self.force_candidate_contact = false;
715
716        if self.connection_state == ConnectionState::Failed {
717            // The connection is currently failed so don't send any checks
718            // In the future it may be restarted though
719            self.last_connection_state = self.connection_state;
720            return;
721        }
722        if self.connection_state == ConnectionState::Checking {
723            // We have just entered checking for the first time so update our checking timer
724            if self.last_connection_state != self.connection_state {
725                self.checking_duration = now;
726            }
727
728            // We have been in checking longer then Disconnect+Failed timeout, set the connection to Failed
729            if now
730                .checked_duration_since(self.checking_duration)
731                .unwrap_or_else(|| Duration::from_secs(0))
732                > self.disconnected_timeout + self.failed_timeout
733            {
734                self.update_connection_state(ConnectionState::Failed);
735                self.last_connection_state = self.connection_state;
736                return;
737            }
738        }
739
740        self.contact_candidates();
741
742        self.last_connection_state = self.connection_state;
743        self.last_checking_time = now;
744    }
745
746    pub(crate) fn update_connection_state(&mut self, new_state: ConnectionState) {
747        if self.connection_state != new_state {
748            // Connection has gone to failed, release all gathered candidates
749            if new_state == ConnectionState::Failed {
750                self.set_selected_pair(None);
751                self.delete_all_candidates(false);
752            }
753
754            info!(
755                "[{}]: Setting new connection state: {}",
756                self.get_name(),
757                new_state
758            );
759            self.connection_state = new_state;
760            self.event_outs
761                .push_back(Event::ConnectionStateChange(new_state));
762        }
763    }
764
765    pub(crate) fn set_selected_pair(&mut self, selected_pair: Option<usize>) {
766        if let Some(pair_index) = selected_pair {
767            trace!(
768                "[{}]: Set selected candidate pair: {:?}",
769                self.get_name(),
770                self.candidate_pairs[pair_index]
771            );
772
773            self.candidate_pairs[pair_index].nominated = true;
774            self.selected_pair = Some(pair_index);
775
776            self.update_connection_state(ConnectionState::Connected);
777
778            // Notify when the selected pair changes
779            let candidate_pair = &self.candidate_pairs[pair_index];
780            self.event_outs
781                .push_back(Event::SelectedCandidatePairChange(
782                    Box::new(self.local_candidates[candidate_pair.local_index].clone()),
783                    Box::new(self.remote_candidates[candidate_pair.remote_index].clone()),
784                ));
785        } else {
786            self.selected_pair = None;
787        }
788    }
789
790    pub(crate) fn ping_all_candidates(&mut self) {
791        let mut pairs: Vec<(usize, usize)> = vec![];
792
793        let name = self.get_name().to_string();
794        if self.candidate_pairs.is_empty() {
795            warn!(
796                "[{}]: pingAllCandidates called with no candidate pairs. Connection is not possible yet.",
797                name,
798            );
799        }
800        for p in &mut self.candidate_pairs {
801            if p.state == CandidatePairState::Waiting {
802                p.state = CandidatePairState::InProgress;
803            } else if p.state != CandidatePairState::InProgress {
804                continue;
805            }
806
807            if p.binding_request_count > self.max_binding_requests {
808                trace!(
809                    "[{}]: max requests reached for pair {} (local_addr {} <-> remote_addr {}), marking it as failed",
810                    name,
811                    *p,
812                    self.local_candidates[p.local_index].addr(),
813                    self.remote_candidates[p.remote_index].addr()
814                );
815                p.state = CandidatePairState::Failed;
816            } else {
817                p.binding_request_count += 1;
818                let local = p.local_index;
819                let remote = p.remote_index;
820                pairs.push((local, remote));
821            }
822        }
823
824        if !pairs.is_empty() {
825            trace!(
826                "[{}]: pinging all {} candidates",
827                self.get_name(),
828                pairs.len()
829            );
830        }
831
832        for (local, remote) in pairs {
833            self.ping_candidate(local, remote);
834        }
835    }
836
837    pub(crate) fn add_pair(&mut self, local_index: usize, remote_index: usize) {
838        let p = CandidatePair::new(
839            local_index,
840            remote_index,
841            self.local_candidates[local_index].priority(),
842            self.remote_candidates[remote_index].priority(),
843            self.is_controlling,
844        );
845        self.candidate_pairs.push(p);
846    }
847
848    pub(crate) fn find_pair(&self, local_index: usize, remote_index: usize) -> Option<usize> {
849        for (index, p) in self.candidate_pairs.iter().enumerate() {
850            if p.local_index == local_index && p.remote_index == remote_index {
851                return Some(index);
852            }
853        }
854        None
855    }
856
857    /// Checks if the selected pair is (still) valid.
858    /// Note: the caller should hold the agent lock.
859    pub(crate) fn validate_selected_pair(&mut self) -> bool {
860        let (valid, disconnected_time) = {
861            self.selected_pair.as_ref().map_or_else(
862                || (false, Duration::from_secs(0)),
863                |&pair_index| {
864                    let remote_index = self.candidate_pairs[pair_index].remote_index;
865
866                    let disconnected_time = Instant::now()
867                        .duration_since(self.remote_candidates[remote_index].last_received());
868                    (true, disconnected_time)
869                },
870            )
871        };
872
873        if valid {
874            // Only allow transitions to fail if a.failedTimeout is non-zero
875            let mut total_time_to_failure = self.failed_timeout;
876            if total_time_to_failure != Duration::from_secs(0) {
877                total_time_to_failure += self.disconnected_timeout;
878            }
879
880            if total_time_to_failure != Duration::from_secs(0)
881                && disconnected_time > total_time_to_failure
882            {
883                self.update_connection_state(ConnectionState::Failed);
884            } else if self.disconnected_timeout != Duration::from_secs(0)
885                && disconnected_time > self.disconnected_timeout
886            {
887                self.update_connection_state(ConnectionState::Disconnected);
888            } else {
889                self.update_connection_state(ConnectionState::Connected);
890            }
891        }
892
893        valid
894    }
895
896    /// Sends STUN Binding Requests to the selected pair at `keepalive_interval` to
897    /// maintain consent freshness (RFC 7675).
898    pub(crate) fn check_keepalive(&mut self) {
899        let (local_index, remote_index, pair_index) = {
900            self.selected_pair
901                .as_ref()
902                .map_or((None, None, None), |&pair_index| {
903                    let p = &self.candidate_pairs[pair_index];
904                    (Some(p.local_index), Some(p.remote_index), Some(pair_index))
905                })
906        };
907
908        if let (Some(local_index), Some(remote_index), Some(pair_index)) =
909            (local_index, remote_index, pair_index)
910            && self.keepalive_interval != Duration::from_secs(0)
911            && self.last_consent_sent.elapsed() >= self.keepalive_interval
912        {
913            self.last_consent_sent = Instant::now();
914            self.candidate_pairs[pair_index].on_consent_request_sent();
915            self.ping_candidate(local_index, remote_index);
916        }
917    }
918
919    fn request_connectivity_check(&mut self) {
920        // Defer the connectivity check to `handle_timeout` instead of running it
921        // inline. Running `contact()` here is re-entrant: it can advance the state
922        // to `Failed`, which calls `delete_all_candidates()` and wipes the candidate
923        // and pair vectors while a caller still holds indices into them (for example
924        // `handle_inbound` while adding a peer-reflexive candidate). See issue #88.
925        self.force_candidate_contact = true;
926    }
927
928    /// Remove all candidates.
929    /// This closes any listening sockets and removes both the local and remote candidate lists.
930    ///
931    /// This is used for restarts, failures and on close.
932    pub(crate) fn delete_all_candidates(&mut self, keep_local_candidates: bool) {
933        if !keep_local_candidates {
934            self.local_candidates.clear();
935        }
936        self.remote_candidates.clear();
937
938        // Candidate pairs reference candidates by index, so once the candidates are
939        // removed every pair - and the `selected_pair`/`nominated_pair` indices into
940        // the pair list - is dangling and must be dropped to avoid out-of-bounds
941        // access. `remote_candidates` is always cleared here, so no pair can remain
942        // valid even when the local candidates are kept. See issue #88.
943        self.candidate_pairs.clear();
944        self.selected_pair = None;
945        self.nominated_pair = None;
946    }
947
948    pub(crate) fn find_remote_candidate(&self, addr: SocketAddr) -> Option<usize> {
949        for (index, c) in self.remote_candidates.iter().enumerate() {
950            if c.addr() == addr {
951                return Some(index);
952            }
953        }
954        None
955    }
956
957    pub(crate) fn find_local_candidate(
958        &self,
959        addr: SocketAddr,
960        transport_protocol: TransportProtocol,
961    ) -> Option<usize> {
962        for (index, c) in self.local_candidates.iter().enumerate() {
963            if c.network_type().to_protocol() != transport_protocol {
964                continue;
965            }
966
967            // For TCP active candidates, match by IP only (ignore port).
968            // TCP active candidates use port 9 as placeholder in signaling,
969            // but the actual connection uses an ephemeral port.
970            if c.tcp_type() == TcpType::Active && transport_protocol == TransportProtocol::TCP {
971                if c.addr().ip() == addr.ip() {
972                    return Some(index);
973                }
974            } else if c.addr() == addr {
975                return Some(index);
976            } else if let Some(related_address) = c.related_address()
977                && related_address.address == addr.ip().to_string()
978                && related_address.port == addr.port()
979            {
980                return Some(index);
981            }
982        }
983        None
984    }
985
986    pub(crate) fn send_binding_request(
987        &mut self,
988        m: &Message,
989        local_index: usize,
990        remote_index: usize,
991    ) {
992        trace!(
993            "[{}]: ping STUN from {} to {}",
994            self.get_name(),
995            self.local_candidates[local_index],
996            self.remote_candidates[remote_index],
997        );
998
999        self.invalidate_pending_binding_requests(Instant::now());
1000
1001        self.pending_binding_requests.push(BindingRequest {
1002            timestamp: Instant::now(),
1003            transaction_id: m.transaction_id,
1004            destination: self.remote_candidates[remote_index].addr(),
1005            is_use_candidate: m.contains(ATTR_USE_CANDIDATE),
1006        });
1007
1008        // Track request sent on the candidate pair
1009        if let Some(pair_index) = self.find_pair(local_index, remote_index) {
1010            self.candidate_pairs[pair_index].on_request_sent();
1011        }
1012
1013        self.send_stun(m, local_index, remote_index);
1014    }
1015
1016    pub(crate) fn send_binding_success(
1017        &mut self,
1018        m: &Message,
1019        local_index: usize,
1020        remote_index: usize,
1021    ) {
1022        let addr = self.remote_candidates[remote_index].addr();
1023        let (ip, port) = (addr.ip(), addr.port());
1024        let local_pwd = self.ufrag_pwd.local_credentials.pwd.clone();
1025
1026        let (out, result) = {
1027            let mut out = Message::new();
1028            let result = out.build(&[
1029                Box::new(m.clone()),
1030                Box::new(BINDING_SUCCESS),
1031                Box::new(XorMappedAddress { ip, port }),
1032                Box::new(MessageIntegrity::new_short_term_integrity(local_pwd)),
1033                Box::new(FINGERPRINT),
1034            ]);
1035            (out, result)
1036        };
1037
1038        if let Err(err) = result {
1039            warn!(
1040                "[{}]: Failed to handle inbound ICE from: {} to: {} error: {}",
1041                self.get_name(),
1042                self.local_candidates[local_index],
1043                self.remote_candidates[remote_index],
1044                err
1045            );
1046        } else {
1047            // Track response sent on the candidate pair
1048            if let Some(pair_index) = self.find_pair(local_index, remote_index) {
1049                self.candidate_pairs[pair_index].on_response_sent();
1050            }
1051            self.send_stun(&out, local_index, remote_index);
1052        }
1053    }
1054
1055    /// Sends a 487 (Role Conflict) error response.
1056    /// RFC 8445 Section 7.3.1.1
1057    pub(crate) fn send_role_conflict_error(
1058        &mut self,
1059        m: &Message,
1060        local_index: usize,
1061        remote_index: usize,
1062    ) {
1063        use stun::error_code::*;
1064
1065        let local_pwd = self.ufrag_pwd.local_credentials.pwd.clone();
1066
1067        let (out, result) = {
1068            let mut out = Message::new();
1069            let result = out.build(&[
1070                Box::new(m.clone()),
1071                Box::new(stun::message::BINDING_ERROR),
1072                Box::new(CODE_ROLE_CONFLICT),
1073                Box::new(MessageIntegrity::new_short_term_integrity(local_pwd)),
1074                Box::new(FINGERPRINT),
1075            ]);
1076            (out, result)
1077        };
1078
1079        if let Err(err) = result {
1080            warn!(
1081                "[{}]: Failed to send role conflict error from: {} to: {} error: {}",
1082                self.get_name(),
1083                self.local_candidates[local_index],
1084                self.remote_candidates[remote_index],
1085                err
1086            );
1087        } else {
1088            debug!(
1089                "[{}]: Sent 487 Role Conflict error from {} to {}",
1090                self.get_name(),
1091                self.local_candidates[local_index],
1092                self.remote_candidates[remote_index]
1093            );
1094            self.send_stun(&out, local_index, remote_index);
1095        }
1096    }
1097
1098    /// Switches the ICE agent role and recomputes all candidate pair priorities.
1099    /// RFC 8445 Section 7.3.1.1
1100    pub(crate) fn switch_role(&mut self) {
1101        self.is_controlling = !self.is_controlling;
1102
1103        // Recompute priorities for all candidate pairs
1104        // The priority calculation depends on ice_role_controlling
1105        for pair in &mut self.candidate_pairs {
1106            pair.ice_role_controlling = self.is_controlling;
1107        }
1108
1109        // Clear nominated pair when switching roles
1110        self.nominated_pair = None;
1111
1112        info!(
1113            "[{}]: Role switched, recomputed {} candidate pair priorities",
1114            self.get_name(),
1115            self.candidate_pairs.len()
1116        );
1117
1118        self.event_outs
1119            .push_back(Event::RoleChange(self.is_controlling));
1120    }
1121
1122    /// Removes pending binding requests that are over `maxBindingRequestTimeout` old Let HTO be the
1123    /// transaction timeout, which SHOULD be 2*RTT if RTT is known or 500 ms otherwise.
1124    ///
1125    /// reference: (IETF ref-8445)[https://tools.ietf.org/html/rfc8445#appendix-B.1].
1126    pub(crate) fn invalidate_pending_binding_requests(&mut self, filter_time: Instant) {
1127        let pending_binding_requests = &mut self.pending_binding_requests;
1128        let initial_size = pending_binding_requests.len();
1129
1130        let mut temp = vec![];
1131        for binding_request in pending_binding_requests.drain(..) {
1132            if filter_time
1133                .checked_duration_since(binding_request.timestamp)
1134                .map(|duration| duration < MAX_BINDING_REQUEST_TIMEOUT)
1135                .unwrap_or(true)
1136            {
1137                temp.push(binding_request);
1138            }
1139        }
1140
1141        *pending_binding_requests = temp;
1142        let bind_requests_remaining = pending_binding_requests.len();
1143        let bind_requests_removed = initial_size - bind_requests_remaining;
1144        if bind_requests_removed > 0 {
1145            trace!(
1146                "[{}]: Discarded {} binding requests because they expired, still {} remaining",
1147                self.get_name(),
1148                bind_requests_removed,
1149                bind_requests_remaining,
1150            );
1151        }
1152    }
1153
1154    /// Assert that the passed `TransactionID` is in our `pendingBindingRequests` and returns the
1155    /// destination, If the bindingRequest was valid remove it from our pending cache.
1156    pub(crate) fn handle_inbound_binding_success(
1157        &mut self,
1158        id: TransactionId,
1159    ) -> Option<BindingRequest> {
1160        self.invalidate_pending_binding_requests(Instant::now());
1161
1162        let pending_binding_requests = &mut self.pending_binding_requests;
1163        for i in 0..pending_binding_requests.len() {
1164            if pending_binding_requests[i].transaction_id == id {
1165                let valid_binding_request = pending_binding_requests.remove(i);
1166                return Some(valid_binding_request);
1167            }
1168        }
1169        None
1170    }
1171
1172    /// Processes STUN traffic from a remote candidate.
1173    pub(crate) fn handle_inbound(
1174        &mut self,
1175        now: Instant,
1176        m: &mut Message,
1177        local_index: usize,
1178        remote_addr: SocketAddr,
1179    ) -> Result<()> {
1180        if m.typ.method != METHOD_BINDING
1181            || !(m.typ.class == CLASS_SUCCESS_RESPONSE
1182                || m.typ.class == CLASS_REQUEST
1183                || m.typ.class == CLASS_INDICATION)
1184        {
1185            trace!(
1186                "[{}]: unhandled STUN from {} to {} class({}) method({})",
1187                self.get_name(),
1188                remote_addr,
1189                self.local_candidates[local_index],
1190                m.typ.class,
1191                m.typ.method
1192            );
1193            return Err(Error::ErrUnhandledStunpacket);
1194        }
1195
1196        // RFC 8445 Section 7.3.1.1 - Detecting and Repairing Role Conflicts
1197        if self.is_controlling {
1198            if m.contains(ATTR_ICE_CONTROLLING) {
1199                // Both agents are controlling - role conflict detected
1200                let mut remote_controlling = crate::attributes::control::AttrControlling::default();
1201                if let Err(err) = remote_controlling.get_from(m) {
1202                    warn!(
1203                        "[{}]: Failed to get remote ICE-CONTROLLING attribute: {}",
1204                        self.get_name(),
1205                        err
1206                    );
1207                    return Err(err);
1208                }
1209
1210                debug!(
1211                    "[{}]: Role conflict detected (both controlling), local tiebreaker: {}, remote tiebreaker: {}",
1212                    self.get_name(),
1213                    self.tie_breaker,
1214                    remote_controlling.0
1215                );
1216
1217                // Only process if this is a request (not a response)
1218                if m.typ.class == CLASS_REQUEST {
1219                    // Send 487 Role Conflict error
1220                    if let Some(remote_index) = self.find_remote_candidate(remote_addr) {
1221                        self.send_role_conflict_error(m, local_index, remote_index);
1222                    }
1223
1224                    // Compare tiebreakers - if ours is smaller, we switch to controlled
1225                    if self.tie_breaker < remote_controlling.0 {
1226                        info!(
1227                            "[{}]: Switching from controlling to controlled due to role conflict (smaller tiebreaker)",
1228                            self.get_name()
1229                        );
1230                        self.switch_role();
1231                    }
1232                }
1233                // Continue processing the message after handling role conflict
1234            } else if m.contains(ATTR_USE_CANDIDATE) {
1235                debug!(
1236                    "[{}]: useCandidate && a.isControlling == true",
1237                    self.get_name(),
1238                );
1239                return Err(Error::ErrUnexpectedStunrequestMessage);
1240            }
1241        } else if m.contains(ATTR_ICE_CONTROLLED) {
1242            // Both agents are controlled - role conflict detected
1243            let mut remote_controlled = crate::attributes::control::AttrControlled::default();
1244            if let Err(err) = remote_controlled.get_from(m) {
1245                warn!(
1246                    "[{}]: Failed to get remote ICE-CONTROLLED attribute: {}",
1247                    self.get_name(),
1248                    err
1249                );
1250                return Err(err);
1251            }
1252
1253            debug!(
1254                "[{}]: Role conflict detected (both controlled), local tiebreaker: {}, remote tiebreaker: {}",
1255                self.get_name(),
1256                self.tie_breaker,
1257                remote_controlled.0
1258            );
1259
1260            // Only process if this is a request (not a response)
1261            if m.typ.class == CLASS_REQUEST {
1262                // Send 487 Role Conflict error
1263                if let Some(remote_index) = self.find_remote_candidate(remote_addr) {
1264                    self.send_role_conflict_error(m, local_index, remote_index);
1265                }
1266
1267                // Compare tiebreakers - if ours is larger, we switch to controlling
1268                if self.tie_breaker > remote_controlled.0 {
1269                    info!(
1270                        "[{}]: Switching from controlled to controlling due to role conflict (larger tiebreaker)",
1271                        self.get_name()
1272                    );
1273                    self.switch_role();
1274                }
1275            }
1276            // Continue processing the message after handling role conflict
1277        }
1278
1279        let Some(remote_credentials) = &self.ufrag_pwd.remote_credentials else {
1280            debug!(
1281                "[{}]: ufrag_pwd.remote_credentials.is_none",
1282                self.get_name(),
1283            );
1284            return Err(Error::ErrPasswordEmpty);
1285        };
1286
1287        let mut remote_candidate_index = self.find_remote_candidate(remote_addr);
1288        if m.typ.class == CLASS_SUCCESS_RESPONSE {
1289            if let Err(err) = assert_inbound_message_integrity(m, remote_credentials.pwd.as_bytes())
1290            {
1291                warn!(
1292                    "[{}]: discard message from ({}), {}",
1293                    self.get_name(),
1294                    remote_addr,
1295                    err
1296                );
1297                return Err(err);
1298            }
1299
1300            if let Some(remote_index) = &remote_candidate_index {
1301                self.handle_success_response(now, m, local_index, *remote_index, remote_addr);
1302            } else {
1303                warn!(
1304                    "[{}]: discard success message from ({}), no such remote",
1305                    self.get_name(),
1306                    remote_addr
1307                );
1308                return Err(Error::ErrUnhandledStunpacket);
1309            }
1310        } else if m.typ.class == CLASS_REQUEST {
1311            {
1312                let username = self.ufrag_pwd.local_credentials.ufrag.clone()
1313                    + ":"
1314                    + remote_credentials.ufrag.as_str();
1315                if let Err(err) = assert_inbound_username(m, &username) {
1316                    warn!(
1317                        "[{}]: discard message from ({}), {}",
1318                        self.get_name(),
1319                        remote_addr,
1320                        err
1321                    );
1322                    return Err(err);
1323                } else if let Err(err) = assert_inbound_message_integrity(
1324                    m,
1325                    self.ufrag_pwd.local_credentials.pwd.as_bytes(),
1326                ) {
1327                    warn!(
1328                        "[{}]: discard message from ({}), {}",
1329                        self.get_name(),
1330                        remote_addr,
1331                        err
1332                    );
1333                    return Err(err);
1334                }
1335            }
1336
1337            if remote_candidate_index.is_none() {
1338                // Use the local candidate's network type for the peer-reflexive candidate
1339                let network_type = self.local_candidates[local_index].network_type();
1340                let (ip, port) = (remote_addr.ip(), remote_addr.port());
1341
1342                let prflx_candidate_config = CandidatePeerReflexiveConfig {
1343                    base_config: CandidateConfig {
1344                        network: network_type.to_string(),
1345                        address: ip.to_string(),
1346                        port,
1347                        component: self.local_candidates[local_index].component(),
1348                        ..CandidateConfig::default()
1349                    },
1350                    rel_addr: "".to_owned(),
1351                    rel_port: 0,
1352                };
1353
1354                match prflx_candidate_config.new_candidate_peer_reflexive() {
1355                    Ok(prflx_candidate) => {
1356                        if let Ok(added) = self.add_remote_candidate(prflx_candidate)
1357                            && added
1358                        {
1359                            // Look the candidate up by address rather than assuming it
1360                            // is the last element: `add_remote_candidate` may not have
1361                            // appended it (e.g. a duplicate), and this stays correct if
1362                            // the vector is ever mutated underneath us. See issue #88.
1363                            remote_candidate_index = self.find_remote_candidate(remote_addr);
1364                        }
1365                    }
1366                    Err(err) => {
1367                        error!(
1368                            "[{}]: Failed to create new remote prflx candidate ({})",
1369                            self.get_name(),
1370                            err
1371                        );
1372                        return Err(err);
1373                    }
1374                };
1375
1376                debug!(
1377                    "[{}]: adding a new peer-reflexive candidate: {} ",
1378                    self.get_name(),
1379                    remote_addr
1380                );
1381            }
1382
1383            trace!(
1384                "[{}]: inbound STUN (Request) from {} to {}",
1385                self.get_name(),
1386                remote_addr,
1387                self.local_candidates[local_index]
1388            );
1389
1390            if let Some(remote_index) = &remote_candidate_index {
1391                self.handle_binding_request(m, local_index, *remote_index);
1392            }
1393        }
1394
1395        if let Some(remote_index) = remote_candidate_index {
1396            self.remote_candidates[remote_index].seen(false);
1397        }
1398
1399        Ok(())
1400    }
1401
1402    // Processes non STUN traffic from a remote candidate, and returns true if it is an actual
1403    // remote candidate.
1404    pub(crate) fn validate_non_stun_traffic(&mut self, remote_addr: SocketAddr) -> bool {
1405        self.find_remote_candidate(remote_addr)
1406            .is_some_and(|remote_index| {
1407                self.remote_candidates[remote_index].seen(false);
1408                true
1409            })
1410    }
1411
1412    pub(crate) fn send_stun(&mut self, msg: &Message, local_index: usize, remote_index: usize) {
1413        let peer_addr = self.remote_candidates[remote_index].addr();
1414        // RFC 8445 §6.1.2: checks for a (server/peer-)reflexive candidate must
1415        // be sent from its base, the bound local socket the candidate was
1416        // derived from; the mapped address is not a local socket.
1417        let local_addr = self.local_candidates[local_index].base_addr();
1418        let transport_protocol = if self.local_candidates[local_index].network_type().is_tcp() {
1419            TransportProtocol::TCP
1420        } else {
1421            TransportProtocol::UDP
1422        };
1423
1424        self.write_outs.push_back(TaggedBytesMut {
1425            now: Instant::now(),
1426            transport: TransportContext {
1427                local_addr,
1428                peer_addr,
1429                ecn: None,
1430                transport_protocol,
1431            },
1432            message: BytesMut::from(&msg.raw[..]),
1433        });
1434
1435        self.local_candidates[local_index].seen(true);
1436    }
1437
1438    fn handle_inbound_candidate_msg(
1439        &mut self,
1440        local_index: usize,
1441        msg: TaggedBytesMut,
1442    ) -> Result<()> {
1443        if is_stun_message(&msg.message) {
1444            let mut m = Message {
1445                raw: msg.message.to_vec(),
1446                ..Message::default()
1447            };
1448
1449            if let Err(err) = m.decode() {
1450                warn!(
1451                    "[{}]: Failed to handle decode ICE from {} to {}: {}",
1452                    self.get_name(),
1453                    msg.transport.local_addr,
1454                    msg.transport.peer_addr,
1455                    err
1456                );
1457                Err(err)
1458            } else {
1459                self.handle_inbound(msg.now, &mut m, local_index, msg.transport.peer_addr)
1460            }
1461        } else {
1462            if !self.validate_non_stun_traffic(msg.transport.peer_addr) {
1463                warn!(
1464                    "[{}]: Discarded message, not a valid remote candidate from {}",
1465                    self.get_name(),
1466                    msg.transport.peer_addr,
1467                );
1468            } else {
1469                warn!(
1470                    "[{}]: non-STUN traffic message from a valid remote candidate from {}",
1471                    self.get_name(),
1472                    msg.transport.peer_addr
1473                );
1474            }
1475            Err(Error::ErrNonStunmessage)
1476        }
1477    }
1478
1479    pub(crate) fn get_name(&self) -> &str {
1480        if self.is_controlling {
1481            "controlling"
1482        } else {
1483            "controlled"
1484        }
1485    }
1486
1487    pub(crate) fn get_selected_pair(&self) -> Option<usize> {
1488        self.selected_pair
1489    }
1490
1491    pub(crate) fn get_best_available_pair(&self) -> Option<usize> {
1492        let mut best_pair_index: Option<usize> = None;
1493
1494        for (index, p) in self.candidate_pairs.iter().enumerate() {
1495            if p.state == CandidatePairState::Failed {
1496                continue;
1497            }
1498
1499            if let Some(pair_index) = &mut best_pair_index {
1500                let b = &self.candidate_pairs[*pair_index];
1501                if b.priority() < p.priority() {
1502                    *pair_index = index;
1503                }
1504            } else {
1505                best_pair_index = Some(index);
1506            }
1507        }
1508
1509        best_pair_index
1510    }
1511
1512    pub(crate) fn get_best_valid_candidate_pair(&self) -> Option<usize> {
1513        let mut best_pair_index: Option<usize> = None;
1514
1515        for (index, p) in self.candidate_pairs.iter().enumerate() {
1516            if p.state != CandidatePairState::Succeeded {
1517                continue;
1518            }
1519
1520            if let Some(pair_index) = &mut best_pair_index {
1521                let b = &self.candidate_pairs[*pair_index];
1522                if b.priority() < p.priority() {
1523                    *pair_index = index;
1524                }
1525            } else {
1526                best_pair_index = Some(index);
1527            }
1528        }
1529
1530        best_pair_index
1531    }
1532}