Skip to main content

rtc_ice/agent/
mod.rs

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