Skip to main content

rice_proto/
conncheck.rs

1// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// SPDX-License-Identifier: MIT OR Apache-2.0
10
11//! Connectivity check module for checking a set of candidates for an appropriate candidate pair to
12//! transfer data with.
13
14use alloc::boxed::Box;
15use alloc::string::String;
16use alloc::vec::Vec;
17
18use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
19use core::net::{IpAddr, SocketAddr};
20use core::ops::Range;
21use core::sync::atomic::{AtomicUsize, Ordering};
22use core::time::Duration;
23use stun_proto::auth::ShortTermAuth;
24
25use crate::ALPHABET;
26use crate::candidate::{Candidate, CandidatePair, CandidateType, TcpType, TransportType};
27use crate::component::ComponentConnectionState;
28use crate::gathering::GatheredCandidate;
29use crate::rand::generate_random_ice_string;
30use crate::tcp::TcpBuffer;
31use crate::turn::TurnClient;
32use byteorder::{BigEndian, ByteOrder};
33use rice_stun_types::attribute::{IceControlled, IceControlling, Priority, UseCandidate};
34use stun_proto::Instant;
35use stun_proto::agent::{StunAgent, StunAgentPollRet, StunError, Transmit};
36use stun_proto::types::attribute::*;
37use stun_proto::types::data::Data;
38use stun_proto::types::message::*;
39use turn_client_proto::api::{Socket5Tuple, TransmitBuild, TurnEvent, TurnPollRet, TurnRecvRet};
40use turn_client_proto::prelude::*;
41
42use tracing::{debug, error, info, trace, warn};
43
44static STUN_AGENT_COUNT: AtomicUsize = AtomicUsize::new(0);
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
47pub(crate) struct StunAgentId(usize);
48
49impl core::ops::Deref for StunAgentId {
50    type Target = usize;
51    fn deref(&self) -> &Self::Target {
52        &self.0
53    }
54}
55
56impl StunAgentId {
57    fn generate() -> Self {
58        let stun_agent_id = STUN_AGENT_COUNT.fetch_add(1, Ordering::SeqCst);
59        Self(stun_agent_id)
60    }
61}
62
63impl core::fmt::Display for StunAgentId {
64    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65        self.0.fmt(f)
66    }
67}
68
69/// ICE Credentials
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct Credentials {
72    /// The username fragment.
73    pub ufrag: String,
74    /// The password.
75    pub passwd: String,
76}
77
78impl From<Credentials> for ShortTermCredentials {
79    fn from(cred: Credentials) -> Self {
80        ShortTermCredentials::new(cred.passwd)
81    }
82}
83
84impl Credentials {
85    /// Constructs a new set of [`Credentials`]
86    pub fn new(username: String, password: String) -> Self {
87        // TODO: validate contents
88        Self {
89            ufrag: username,
90            passwd: password,
91        }
92    }
93}
94
95#[derive(Debug, Clone)]
96pub struct SelectedTurn {
97    transport: TransportType,
98    local_addr: SocketAddr,
99    remote_addr: SocketAddr,
100}
101
102impl SelectedTurn {
103    pub fn transport(&self) -> TransportType {
104        self.transport
105    }
106    pub fn local_addr(&self) -> SocketAddr {
107        self.local_addr
108    }
109    pub fn remote_addr(&self) -> SocketAddr {
110        self.remote_addr
111    }
112}
113
114/// A pair that has been selected for a component
115#[derive(Debug, Clone)]
116pub struct SelectedPair {
117    candidate_pair: CandidatePair,
118    local_stun_agent: StunAgentId,
119    turn: Option<SelectedTurn>,
120}
121
122impl SelectedPair {
123    /// Create a new [`SelectedPair`].  The pair and stun agent must be compatible.
124    pub(crate) fn new(
125        candidate_pair: CandidatePair,
126        local_stun_agent: StunAgentId,
127        turn: Option<SelectedTurn>,
128    ) -> Self {
129        Self {
130            candidate_pair,
131            local_stun_agent,
132            turn,
133        }
134    }
135
136    /// The pair for this [`SelectedPair`]
137    pub fn candidate_pair(&self) -> &CandidatePair {
138        &self.candidate_pair
139    }
140
141    /// Any TURN connection the local candidate must be connected through.
142    pub fn local_turn(&self) -> Option<&SelectedTurn> {
143        self.turn.as_ref()
144    }
145
146    /// The local STUN agent for this [`SelectedPair`]
147    pub(crate) fn stun_agent_id(&self) -> StunAgentId {
148        self.local_stun_agent
149    }
150}
151
152#[derive(Debug, Clone, PartialEq, Eq)]
153pub struct RequestRto {
154    pub(crate) initial: Duration,
155    pub(crate) max: Duration,
156    pub(crate) retransmits: u32,
157    pub(crate) final_retransmit_timeout: Duration,
158}
159
160impl RequestRto {
161    pub fn from_parts(
162        initial: Duration,
163        max: Duration,
164        retransmits: u32,
165        final_retransmit_timeout: Duration,
166    ) -> Self {
167        Self {
168            initial,
169            max,
170            retransmits,
171            final_retransmit_timeout,
172        }
173    }
174}
175
176/// Return value when handling received data
177#[derive(Debug)]
178pub struct HandleRecvReply<T: AsRef<[u8]> + core::fmt::Debug> {
179    pub handled: bool,
180    pub have_more_data: bool,
181    pub data: Option<DataAndRange<T>>,
182}
183
184impl<T: AsRef<[u8]> + core::fmt::Debug> core::fmt::Display for HandleRecvReply<T> {
185    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186        write!(f, "HandleRecvReply {{")?;
187        let mut need_comma = false;
188        if self.handled {
189            write!(f, "handled, ")?;
190            need_comma = true;
191        }
192        if let Some(data) = self.data.as_ref() {
193            if need_comma {
194                write!(f, ", ")?;
195            }
196            write!(f, "{} bytes", data.as_ref().len())?;
197        }
198        write!(f, "}}")
199    }
200}
201
202impl<T: AsRef<[u8]> + core::fmt::Debug> Default for HandleRecvReply<T> {
203    fn default() -> Self {
204        Self {
205            handled: false,
206            have_more_data: false,
207            data: None,
208        }
209    }
210}
211
212#[derive(Debug)]
213pub struct DataAndRange<T: AsRef<[u8]> + core::fmt::Debug> {
214    data: T,
215    range: Range<usize>,
216}
217
218impl<T: AsRef<[u8]> + core::fmt::Debug> AsRef<[u8]> for DataAndRange<T> {
219    fn as_ref(&self) -> &[u8] {
220        &self.data.as_ref()[self.range.start..self.range.end]
221    }
222}
223
224/// Events that can be produced during the connectivity check process
225#[derive(Debug)]
226pub enum ConnCheckEvent {
227    /// The state of a component has changed
228    ComponentState(usize, ComponentConnectionState),
229    /// A component has chosen a pair.  This pair should be used to send and receive data from.
230    SelectedPair(usize, Box<SelectedPair>),
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub(crate) enum CandidatePairState {
235    Waiting,
236    InProgress,
237    Succeeded,
238    Failed,
239    Frozen,
240}
241
242impl CandidatePairState {
243    fn cmp_progression(&self, other: &Self) -> core::cmp::Ordering {
244        if self == other {
245            return core::cmp::Ordering::Equal;
246        }
247        match (self, other) {
248            (Self::Failed, _) => core::cmp::Ordering::Less,
249            (Self::Frozen, other) => {
250                if *other == Self::Failed {
251                    core::cmp::Ordering::Greater
252                } else {
253                    core::cmp::Ordering::Less
254                }
255            }
256            (Self::Waiting, other) => {
257                if [Self::Failed, Self::Frozen].contains(other) {
258                    core::cmp::Ordering::Greater
259                } else {
260                    core::cmp::Ordering::Less
261                }
262            }
263            (Self::InProgress, other) => {
264                if [Self::Failed, Self::Frozen, Self::Waiting].contains(other) {
265                    core::cmp::Ordering::Greater
266                } else {
267                    core::cmp::Ordering::Less
268                }
269            }
270            (Self::Succeeded, other) => {
271                if [Self::Failed, Self::Frozen, Self::InProgress, Self::Waiting].contains(other) {
272                    core::cmp::Ordering::Greater
273                } else {
274                    core::cmp::Ordering::Less
275                }
276            }
277        }
278    }
279}
280
281impl core::fmt::Display for CandidatePairState {
282    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
283        f.pad(&alloc::format!("{self:?}"))
284    }
285}
286
287static CONN_CHECK_COUNT: AtomicUsize = AtomicUsize::new(0);
288
289#[derive(Debug, Clone)]
290struct TcpConnCheck {
291    agent: Option<StunAgentId>,
292}
293
294#[derive(Debug, Clone)]
295enum ConnCheckVariant {
296    Agent(StunAgentId),
297    Tcp(TcpConnCheck),
298}
299
300#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
301struct ConnCheckId(usize);
302
303impl core::ops::Deref for ConnCheckId {
304    type Target = usize;
305    fn deref(&self) -> &Self::Target {
306        &self.0
307    }
308}
309
310impl ConnCheckId {
311    fn generate() -> Self {
312        let conncheck_id = CONN_CHECK_COUNT.fetch_add(1, Ordering::SeqCst);
313        Self(conncheck_id)
314    }
315}
316
317impl core::fmt::Display for ConnCheckId {
318    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
319        self.0.fmt(f)
320    }
321}
322
323#[derive(Debug)]
324struct ConnCheck {
325    conncheck_id: ConnCheckId,
326    checklist_id: usize,
327    nominate: bool,
328    pair: CandidatePair,
329    variant: ConnCheckVariant,
330    controlling: bool,
331    state: CandidatePairState,
332    stun_request: Option<TransactionId>,
333    remote_credentials: Credentials,
334}
335
336impl ConnCheck {
337    fn new(
338        checklist_id: usize,
339        pair: CandidatePair,
340        agent: StunAgentId,
341        nominate: bool,
342        controlling: bool,
343        remote_credentials: Credentials,
344    ) -> Self {
345        Self {
346            conncheck_id: ConnCheckId::generate(),
347            checklist_id,
348            pair,
349            state: CandidatePairState::Frozen,
350            stun_request: None,
351            variant: ConnCheckVariant::Agent(agent),
352            nominate,
353            controlling,
354            remote_credentials,
355        }
356    }
357
358    fn new_tcp(
359        checklist_id: usize,
360        pair: CandidatePair,
361        nominate: bool,
362        controlling: bool,
363        remote_credentials: Credentials,
364    ) -> Self {
365        Self {
366            conncheck_id: ConnCheckId::generate(),
367            checklist_id,
368            pair,
369            state: CandidatePairState::Frozen,
370            stun_request: None,
371            variant: ConnCheckVariant::Tcp(TcpConnCheck { agent: None }),
372            nominate,
373            controlling,
374            remote_credentials,
375        }
376    }
377
378    fn agent_id(&self) -> Option<StunAgentId> {
379        match &self.variant {
380            ConnCheckVariant::Agent(agent) => Some(*agent),
381            ConnCheckVariant::Tcp(tcp) => tcp.agent,
382        }
383    }
384
385    fn state(&self) -> CandidatePairState {
386        self.state
387    }
388
389    #[tracing::instrument(
390        name = "set_check_state",
391        level = "debug",
392        skip(self, state),
393        fields(
394            ?self.conncheck_id,
395        )
396    )]
397    fn set_state(&mut self, state: CandidatePairState) {
398        // TODO: validate state change
399        if self.state != state {
400            debug!(old_state = ?self.state, new_state = ?state, "updating state");
401            self.state = state;
402        }
403    }
404
405    fn nominate(&self) -> bool {
406        self.nominate
407    }
408
409    fn generate_stun_request(
410        pair: &CandidatePair,
411        nominate: bool,
412        controlling: bool,
413        tie_breaker: u64,
414        local_credentials: Credentials,
415        remote_credentials: Credentials,
416    ) -> Result<MessageWriteVec, StunError> {
417        let username = remote_credentials.ufrag.clone() + ":" + &local_credentials.ufrag;
418
419        // XXX: this needs to be the priority as if the candidate was peer-reflexive
420        let mut msg = Message::builder_request(BINDING, MessageWriteVec::new());
421        let priority = Priority::new(pair.local.priority);
422        msg.add_attribute(&priority)?;
423        let control = IceControlling::new(tie_breaker);
424        let controlled = IceControlled::new(tie_breaker);
425        if controlling {
426            msg.add_attribute(&control)?;
427        } else {
428            msg.add_attribute(&controlled)?;
429        }
430        let use_cand = UseCandidate::new();
431        if nominate {
432            msg.add_attribute(&use_cand)?;
433        }
434        let username = Username::new(&username)?;
435        msg.add_attribute(&username)?;
436        msg.add_message_integrity(
437            &MessageIntegrityCredentials::ShortTerm(remote_credentials.clone().into()),
438            IntegrityAlgorithm::Sha1,
439        )?;
440        msg.add_fingerprint()?;
441        Ok(msg)
442    }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
446pub(crate) enum CheckListState {
447    Running,
448    Completed,
449    Failed,
450}
451
452static CONN_CHECK_LIST_COUNT: AtomicUsize = AtomicUsize::new(0);
453
454#[derive(Debug)]
455struct CheckStunAgent {
456    id: StunAgentId,
457    agent: StunAgent,
458    turn_id: Option<StunAgentId>,
459}
460
461/// A list of connectivity checks for an ICE stream
462#[derive(Debug)]
463pub struct ConnCheckList {
464    checklist_id: usize,
465    state: CheckListState,
466    component_ids: Vec<(usize, ComponentConnectionState)>,
467    local_credentials: Credentials,
468    remote_credentials: Credentials,
469    local_candidates: Vec<ConnCheckLocalCandidate>,
470    remote_candidates: Vec<Candidate>,
471    // TODO: move to BinaryHeap or similar
472    triggered: VecDeque<ConnCheckId>,
473    pairs: VecDeque<ConnCheck>,
474    valid: Vec<ConnCheckId>,
475    nominated: Vec<ConnCheckId>,
476    controlling: bool,
477    trickle_ice: bool,
478    local_end_of_candidates: bool,
479    remote_end_of_candidates: bool,
480    events: VecDeque<ConnCheckEvent>,
481    agents: Vec<CheckStunAgent>,
482    stun_auth_local: ShortTermAuth,
483    stun_auth_remote: ShortTermAuth,
484    turn_clients: Vec<CheckTurnClient>,
485    pending_delete_turn_clients: Vec<CheckTurnClient>,
486    tcp_buffers: BTreeMap<(SocketAddr, SocketAddr), TcpBuffer>,
487    pending_turn_permissions: VecDeque<(StunAgentId, TransportType, IpAddr)>,
488    pending_recv: VecDeque<PendingRecv>,
489    pending_turn_tcp_connect: Vec<PendingTurnTcp>,
490}
491
492#[derive(Debug)]
493struct CheckTurnClient {
494    id: StunAgentId,
495    client: TurnClient,
496    local_tcp_sockets: Vec<SocketAddr>,
497}
498
499#[derive(Debug)]
500struct PendingTurnTcp {
501    turn_id: StunAgentId,
502    conncheck_id: ConnCheckId,
503    relayed_addr: SocketAddr,
504    turn_addr: SocketAddr,
505    peer_addr: SocketAddr,
506    turn_connect_id: Option<u32>,
507    allocated: bool,
508}
509
510fn candidate_is_same_connection(a: &Candidate, b: &Candidate) -> bool {
511    if a.component_id != b.component_id {
512        return false;
513    }
514    if a.transport_type != b.transport_type {
515        return false;
516    }
517    if a.base_address != b.base_address {
518        return false;
519    }
520    if a.address != b.address {
521        return false;
522    }
523    // TODO: active vs passive vs simultaneous open
524    if a.tcp_type != b.tcp_type {
525        return false;
526    }
527    // XXX: extensions?
528    true
529}
530
531fn candidate_pair_is_same_connection(a: &CandidatePair, b: &CandidatePair) -> bool {
532    if !candidate_is_same_connection(&a.local, &b.local) {
533        return false;
534    }
535    if !candidate_is_same_connection(&a.remote, &b.remote) {
536        return false;
537    }
538    true
539}
540
541#[derive(Debug)]
542enum LocalCandidateVariant {
543    Agent(StunAgentId),
544    TcpListener,
545    TcpActive,
546}
547
548#[derive(Debug)]
549struct ConnCheckLocalCandidate {
550    candidate: Candidate,
551    variant: LocalCandidateVariant,
552}
553
554fn response_add_credentials<O, B: MessageWrite<Output = O>>(
555    response: B,
556    auth: &mut ShortTermAuth,
557) -> Result<B, StunError> {
558    let mut response = auth.sign_outgoing_message(response).unwrap();
559    response.add_fingerprint()?;
560    Ok(response)
561}
562fn binding_success_response(
563    msg: &Message<'_>,
564    from: SocketAddr,
565    auth: &mut ShortTermAuth,
566) -> MessageWriteVec {
567    let mut response = Message::builder_success(msg, MessageWriteVec::new());
568    let xor_addr = XorMappedAddress::new(from, msg.transaction_id());
569    response.add_attribute(&xor_addr).unwrap();
570    response_add_credentials(response, auth).unwrap()
571}
572
573#[derive(Clone, Copy, Debug)]
574enum Nominate {
575    True,
576    False,
577    DontCare,
578}
579
580impl PartialEq<Nominate> for Nominate {
581    fn eq(&self, other: &Nominate) -> bool {
582        matches!(self, &Nominate::DontCare)
583            || matches!(other, &Nominate::DontCare)
584            || (matches!(self, Nominate::True) && matches!(other, Nominate::True))
585            || (matches!(self, Nominate::False) && matches!(other, Nominate::False))
586    }
587}
588impl PartialEq<bool> for Nominate {
589    fn eq(&self, other: &bool) -> bool {
590        matches!(self, Nominate::DontCare)
591            || (*other && self.eq(&Nominate::True))
592            || (!*other && self.eq(&Nominate::False))
593    }
594}
595
596fn generate_random_credentials() -> Credentials {
597    let user = generate_random_ice_string(ALPHABET.as_bytes(), 4);
598    let pass = generate_random_ice_string(ALPHABET.as_bytes(), 22);
599    Credentials::new(user, pass)
600}
601
602impl ConnCheckList {
603    fn new(checklist_id: usize, controlling: bool, trickle_ice: bool) -> Self {
604        let local_credentials = generate_random_credentials();
605        let remote_credentials = generate_random_credentials();
606        let mut stun_auth_local = ShortTermAuth::new();
607        stun_auth_local.set_credentials(local_credentials.clone().into(), IntegrityAlgorithm::Sha1);
608        let mut stun_auth_remote = ShortTermAuth::new();
609        stun_auth_remote
610            .set_credentials(remote_credentials.clone().into(), IntegrityAlgorithm::Sha1);
611        Self {
612            checklist_id,
613            state: CheckListState::Running,
614            component_ids: Vec::new(),
615            local_credentials,
616            remote_credentials,
617            local_candidates: Vec::new(),
618            remote_candidates: Vec::new(),
619            triggered: VecDeque::new(),
620            pairs: VecDeque::new(),
621            valid: Vec::new(),
622            nominated: Vec::new(),
623            controlling,
624            trickle_ice,
625            local_end_of_candidates: false,
626            remote_end_of_candidates: false,
627            events: VecDeque::new(),
628            agents: Vec::new(),
629            stun_auth_local,
630            stun_auth_remote,
631            turn_clients: Vec::new(),
632            pending_delete_turn_clients: Vec::new(),
633            tcp_buffers: BTreeMap::default(),
634            pending_turn_permissions: VecDeque::new(),
635            pending_recv: VecDeque::new(),
636            pending_turn_tcp_connect: Vec::new(),
637        }
638    }
639
640    fn state(&self) -> CheckListState {
641        self.state
642    }
643
644    /// Set the local [`Credentials`] for this checklist
645    pub fn set_local_credentials(&mut self, credentials: Credentials) {
646        trace!(
647            "changing local credentials from {:?} to {credentials:?}",
648            self.remote_credentials
649        );
650        self.stun_auth_local
651            .set_credentials(credentials.clone().into(), IntegrityAlgorithm::Sha1);
652        self.local_credentials = credentials;
653    }
654
655    /// Set the remote [`Credentials`] for this checklist
656    pub fn set_remote_credentials(&mut self, credentials: Credentials) {
657        trace!(
658            "changing remote credentials from {:?} to {credentials:?}",
659            self.remote_credentials
660        );
661        self.stun_auth_remote
662            .set_credentials(credentials.clone().into(), IntegrityAlgorithm::Sha1);
663
664        // If we already have checks that are using old or outdated credentials, replace them with
665        // new checks that use the new remote credentials
666        let mut request_cancels = Vec::new();
667        let new_pairs = self
668            .pairs
669            .drain(..)
670            .map(|mut check| {
671                if check.remote_credentials != credentials {
672                    if let Some((agent_id, request_id)) =
673                        check.agent_id().zip(check.stun_request.take())
674                    {
675                        request_cancels.push((agent_id, request_id));
676                        let mut check = ConnCheck::new(
677                            check.checklist_id,
678                            check.pair.clone(),
679                            agent_id,
680                            check.nominate(),
681                            check.controlling,
682                            credentials.clone(),
683                        );
684                        if check.state != CandidatePairState::Frozen {
685                            check.set_state(CandidatePairState::Waiting);
686                        }
687                        if let ConnCheckVariant::Tcp(ref mut tcp) = check.variant {
688                            tcp.agent.take();
689                        }
690                        check
691                    } else {
692                        check
693                    }
694                } else {
695                    check
696                }
697            })
698            .collect::<VecDeque<_>>();
699        self.pairs = new_pairs;
700        self.sort_pairs();
701
702        for (agent_id, request_id) in request_cancels {
703            let Some(agent) = self.mut_agent_by_id(agent_id) else {
704                continue;
705            };
706            let Some(mut request) = agent.mut_request_transaction(request_id) else {
707                continue;
708            };
709            request.cancel();
710        }
711
712        self.remote_credentials = credentials;
713    }
714
715    /// Add a component id to this checklist
716    pub fn add_component(&mut self, component_id: usize) {
717        if self
718            .component_ids
719            .iter()
720            .any(|(cid, _state)| component_id == *cid)
721        {
722            panic!("Component with ID {component_id} already exists in checklist!");
723        };
724        self.component_ids
725            .push((component_id, ComponentConnectionState::New));
726    }
727
728    fn poll_event(&mut self) -> Option<ConnCheckEvent> {
729        self.events.pop_back()
730    }
731
732    pub(crate) fn poll_recv(&mut self) -> Option<PendingRecv> {
733        self.pending_recv.pop_front()
734    }
735
736    pub(crate) fn add_agent_for_5tuple(
737        &mut self,
738        transport: TransportType,
739        local: SocketAddr,
740        remote: SocketAddr,
741        turn_id: Option<StunAgentId>,
742    ) -> (StunAgentId, usize) {
743        let agent = new_agent(transport, local, remote);
744        self.add_agent(agent, turn_id)
745    }
746
747    fn add_agent(
748        &mut self,
749        agent: StunAgent,
750        turn_id: Option<StunAgentId>,
751    ) -> (StunAgentId, usize) {
752        let agent_id = StunAgentId::generate();
753        trace!(
754            "Adding STUN Agent with id {agent_id} for transport {}, remote:{:?}, local:{}, turn:{turn_id:?}",
755            agent.transport(),
756            agent.remote_addr(),
757            agent.local_addr()
758        );
759        self.agents.push(CheckStunAgent {
760            id: agent_id,
761            agent,
762            turn_id,
763        });
764        (agent_id, self.agents.len() - 1)
765    }
766
767    pub(crate) fn mut_agent_for_5tuple(
768        &mut self,
769        transport: TransportType,
770        local: SocketAddr,
771        remote: SocketAddr,
772    ) -> Option<(StunAgentId, &mut StunAgent)> {
773        self.agents.iter_mut().find_map(|a| {
774            let matched = match transport {
775                TransportType::Udp => {
776                    a.agent.local_addr() == local && a.agent.transport() == TransportType::Udp
777                }
778                TransportType::Tcp => {
779                    a.agent.local_addr() == local
780                        && a.agent.transport() == TransportType::Tcp
781                        && a.agent.remote_addr().unwrap() == remote
782                }
783            };
784            if matched {
785                Some((a.id, &mut a.agent))
786            } else {
787                None
788            }
789        })
790    }
791
792    pub(crate) fn find_agent_for_5tuple(
793        &self,
794        transport: TransportType,
795        local: SocketAddr,
796        remote: SocketAddr,
797    ) -> Option<(StunAgentId, &StunAgent)> {
798        self.agents.iter().find_map(|a| {
799            let matched = match transport {
800                TransportType::Udp => {
801                    a.agent.local_addr() == local && a.agent.transport() == TransportType::Udp
802                }
803                TransportType::Tcp => {
804                    a.agent.local_addr() == local
805                        && a.agent.transport() == TransportType::Tcp
806                        && a.agent.remote_addr().unwrap() == remote
807                }
808            };
809            if matched {
810                Some((a.id, &a.agent))
811            } else {
812                None
813            }
814        })
815    }
816
817    pub(crate) fn agent_by_id(&self, id: StunAgentId) -> Option<&StunAgent> {
818        self.agents.iter().find_map(|agent| {
819            if id == agent.id {
820                Some(&agent.agent)
821            } else {
822                None
823            }
824        })
825    }
826
827    pub(crate) fn mut_agent_by_id(&mut self, id: StunAgentId) -> Option<&mut StunAgent> {
828        self.agents.iter_mut().find_map(|agent| {
829            if id == agent.id {
830                Some(&mut agent.agent)
831            } else {
832                None
833            }
834        })
835    }
836
837    pub(crate) fn find_or_create_udp_agent(
838        &mut self,
839        candidate: &Candidate,
840        turn_id: Option<StunAgentId>,
841    ) -> (StunAgentId, &StunAgent) {
842        if let Some(agent_id) = self
843            .agents
844            .iter()
845            .find(|a| {
846                let a = &a.agent;
847                match candidate.transport_type {
848                    TransportType::Udp => {
849                        a.local_addr() == candidate.base_address
850                            && a.transport() == TransportType::Udp
851                    }
852                    _ => false,
853                }
854            })
855            .map(|a| a.id)
856        {
857            return (agent_id, self.agent_by_id(agent_id).unwrap());
858        }
859        let agent = StunAgent::builder(candidate.transport_type, candidate.base_address).build();
860        let (agent_id, agent_idx) = self.add_agent(agent, turn_id);
861        (agent_id, &self.agents[agent_idx].agent)
862    }
863
864    fn mut_turn_client_by_id(&mut self, id: StunAgentId) -> Option<&mut TurnClient> {
865        self.turn_clients
866            .iter_mut()
867            .chain(self.pending_delete_turn_clients.iter_mut())
868            .find_map(|client| {
869                if id == client.id {
870                    Some(&mut client.client)
871                } else {
872                    None
873                }
874            })
875    }
876
877    fn remove_turn_client_by_id(&mut self, id: StunAgentId) -> Option<TurnClient> {
878        if let Some(position) = self.turn_clients.iter().position(|client| id == client.id) {
879            Some(self.turn_clients.remove(position).client)
880        } else {
881            None
882        }
883    }
884
885    fn turn_client_by_allocated_address(
886        &self,
887        allocated_transport: TransportType,
888        allocated_addr: SocketAddr,
889    ) -> Option<(StunAgentId, &TurnClient)> {
890        self.turn_clients.iter().find_map(|client| {
891            if client
892                .client
893                .relayed_addresses()
894                .any(|(client_relayed_transport, relayed)| {
895                    // XXX: what about ipv4->6 (and reverse) translators between the turn
896                    // server and the peer.
897                    allocated_transport == client_relayed_transport && relayed == allocated_addr
898                })
899            {
900                Some((client.id, &client.client))
901            } else {
902                None
903            }
904        })
905    }
906
907    pub(crate) fn mut_turn_client_by_allocated_address(
908        &mut self,
909        allocated_transport: TransportType,
910        allocated_addr: SocketAddr,
911    ) -> Option<(StunAgentId, &mut TurnClient)> {
912        self.turn_clients
913            .iter_mut()
914            .chain(self.pending_delete_turn_clients.iter_mut())
915            .find_map(|client| {
916                if client
917                    .client
918                    .relayed_addresses()
919                    .any(|(client_relayed_transport, relayed)| {
920                        // XXX: what about ipv4->6 (and reverse) translators between the turn
921                        // server and the peer.
922                        allocated_transport == client_relayed_transport && relayed == allocated_addr
923                    })
924                {
925                    Some((client.id, &mut client.client))
926                } else {
927                    None
928                }
929            })
930    }
931
932    pub(crate) fn find_turn_client_for_5tuple(
933        &self,
934        transport: TransportType,
935        local: SocketAddr,
936        remote: SocketAddr,
937    ) -> Option<(StunAgentId, &TurnClient)> {
938        self.turn_clients
939            .iter()
940            .chain(self.pending_delete_turn_clients.iter())
941            .find_map(|client| {
942                if client.client.transport() == transport
943                    && client.client.remote_addr() == remote
944                    && (client.client.local_addr() == local
945                        || client.local_tcp_sockets.contains(&local))
946                {
947                    Some((client.id, &client.client))
948                } else {
949                    None
950                }
951            })
952    }
953
954    /// Add a local candidate to this checklist.
955    ///
956    /// Should call the `poll` loop at the next earliest opportunity.
957    ///
958    /// # Panics
959    ///
960    /// - end_of_local_candidates() has already been called
961    /// - add_component() for the candidate has not been called
962    #[tracing::instrument(
963        level = "info"
964        skip(self, gathered),
965        fields(
966            checklist_id = self.checklist_id,
967            candidate = ?gathered.candidate,
968        )
969    )]
970    pub fn add_local_gathered_candidate(&mut self, gathered: GatheredCandidate) -> bool {
971        let candidate_type = gathered.candidate.candidate_type;
972        let turn_id = if candidate_type == CandidateType::Relayed {
973            Some(StunAgentId::generate())
974        } else {
975            None
976        };
977        if !self.add_local_candidate_internal(gathered.candidate, turn_id) {
978            return false;
979        }
980        if candidate_type == CandidateType::Relayed {
981            let client = gathered.turn_agent.unwrap();
982            self.turn_clients.push(CheckTurnClient {
983                id: turn_id.unwrap(),
984                client: *client,
985                local_tcp_sockets: Default::default(),
986            });
987        }
988        self.generate_checks();
989        true
990    }
991
992    /// Add a local candidate to this checklist.
993    ///
994    /// Should call the `poll` loop at the next earliest opportunity.
995    ///
996    /// # Panics
997    ///
998    /// - end_of_local_candidates() has already been called
999    /// - add_component() for the candidate has not been called
1000    #[tracing::instrument(
1001        level = "info"
1002        skip(self),
1003        fields(
1004            checklist_id = self.checklist_id,
1005        )
1006    )]
1007    pub fn add_local_candidate(&mut self, local: Candidate) -> bool {
1008        if self.add_local_candidate_internal(local, None) {
1009            self.generate_checks();
1010            true
1011        } else {
1012            false
1013        }
1014    }
1015
1016    fn add_local_candidate_internal(
1017        &mut self,
1018        local: Candidate,
1019        turn_id: Option<StunAgentId>,
1020    ) -> bool {
1021        if self.local_end_of_candidates {
1022            panic!("Attempt made to add a local candidate after end-of-candidate received");
1023        }
1024        let existing = self
1025            .component_ids
1026            .iter()
1027            .find(|(id, _state)| id == &local.component_id);
1028        if existing.is_none() {
1029            panic!("Attempt made to add a local candidate without a corresponding add_component()");
1030        }
1031
1032        if let Some(idx) = self
1033            .local_candidates
1034            .iter()
1035            .position(|c| local.redundant_with(&c.candidate))
1036        {
1037            let other = &self.local_candidates[idx].candidate;
1038            if self.trickle_ice || other.priority >= local.priority {
1039                debug!("not adding redundant candidate");
1040                return false;
1041            } else if !self.trickle_ice {
1042                let removed = self.local_candidates.swap_remove(idx);
1043                debug!(
1044                    "removing already existing redundant candidate {:?}",
1045                    removed.candidate
1046                );
1047                // FIXME: this may remove in flight checks
1048                self.pairs
1049                    .retain(|pair| pair.pair.local != removed.candidate);
1050                // TODO: signal potential socket/agent removal?
1051            }
1052        }
1053
1054        info!("adding");
1055
1056        match local.transport_type {
1057            TransportType::Udp => {
1058                let (agent_id, _) = self.find_or_create_udp_agent(&local, turn_id);
1059                self.local_candidates.push(ConnCheckLocalCandidate {
1060                    candidate: local,
1061                    variant: LocalCandidateVariant::Agent(agent_id),
1062                });
1063            }
1064            TransportType::Tcp => {
1065                let tcp_type = local.tcp_type.unwrap();
1066                if matches!(tcp_type, TcpType::Passive) {
1067                    self.local_candidates.push(ConnCheckLocalCandidate {
1068                        candidate: local,
1069                        variant: LocalCandidateVariant::TcpListener,
1070                    });
1071                } else {
1072                    self.local_candidates.push(ConnCheckLocalCandidate {
1073                        candidate: local,
1074                        variant: LocalCandidateVariant::TcpActive,
1075                    });
1076                }
1077            }
1078        }
1079        true
1080    }
1081
1082    /// Signal to the checklist that no more local candidates will be provided.
1083    #[tracing::instrument(
1084        level = "debug",
1085        skip(self),
1086        fields(
1087            checklist_id = self.checklist_id,
1088        )
1089    )]
1090    pub fn end_of_local_candidates(&mut self) {
1091        info!("end of local candidates");
1092        self.local_end_of_candidates = true;
1093        if self.remote_end_of_candidates {
1094            self.check_for_failure();
1095        }
1096        self.dump_check_state();
1097    }
1098
1099    /// Signal to the checklist that no more remote candidates will be provided
1100    #[tracing::instrument(
1101        level = "debug",
1102        skip(self),
1103        fields(
1104            checklist_id = self.checklist_id,
1105        )
1106    )]
1107    pub fn end_of_remote_candidates(&mut self) {
1108        info!("end of remote candidates");
1109        self.remote_end_of_candidates = true;
1110        if self.local_end_of_candidates {
1111            self.check_for_failure();
1112        }
1113        self.dump_check_state();
1114    }
1115
1116    /// Add a remote candidate to the checklist
1117    pub fn add_remote_candidate(&mut self, remote: Candidate) {
1118        if remote.candidate_type != CandidateType::PeerReflexive && self.remote_end_of_candidates {
1119            error!("Attempt made to add a remote candidate after an end-of-candidates received");
1120            return;
1121        }
1122        if !self
1123            .component_ids
1124            .iter()
1125            .any(|(cid, _state)| cid == &remote.component_id)
1126        {
1127            self.component_ids
1128                .push((remote.component_id, ComponentConnectionState::New));
1129        }
1130        self.remote_candidates.push(remote);
1131        self.generate_checks();
1132        self.dump_check_state();
1133    }
1134
1135    fn next_triggered(&mut self) -> Option<&mut ConnCheck> {
1136        // triggered checks referenced by these ids may be removed before the check has a chance to
1137        // start.  Simply remove them and continue processing.
1138        while let Some(check_id) = self.triggered.pop_back() {
1139            if let Some(check) = self.mut_check_by_id(check_id) {
1140                // Don't trigger this check if there is already a STUN request in progress.
1141                // Can happen on remote input if the same check is triggered while the check is
1142                // sent to the peer.
1143                if check.stun_request.is_some() {
1144                    continue;
1145                }
1146                check.set_state(CandidatePairState::InProgress);
1147                return self.mut_check_by_id(check_id);
1148            }
1149        }
1150        None
1151    }
1152
1153    #[cfg(test)]
1154    fn is_triggered(&self, needle: &CandidatePair) -> bool {
1155        trace!("triggered {:?}", self.triggered);
1156        self.triggered.iter().any(|&check_id| {
1157            self.check_by_id(check_id)
1158                .is_some_and(|check| needle == &check.pair)
1159        })
1160    }
1161
1162    #[tracing::instrument(
1163        level = "debug",
1164        skip(self),
1165        fields(
1166            checklist_id = self.checklist_id
1167        )
1168    )]
1169    fn next_waiting(&mut self) -> Option<&mut ConnCheck> {
1170        self.pairs
1171            .iter_mut()
1172            // first look for any that are waiting
1173            // FIXME: should be highest priority pair: make the data structure give us that by
1174            // default
1175            .filter_map(|check| {
1176                if check.state == CandidatePairState::Waiting {
1177                    check.set_state(CandidatePairState::InProgress);
1178                    Some(check)
1179                } else {
1180                    None
1181                }
1182            })
1183            .next()
1184    }
1185
1186    fn foundations(&self) -> BTreeSet<String> {
1187        let mut foundations = BTreeSet::new();
1188        let _: Vec<_> = self
1189            .pairs
1190            .iter()
1191            .inspect(|check| {
1192                foundations.insert(check.pair.foundation());
1193            })
1194            .collect();
1195        foundations
1196    }
1197
1198    fn check_has_turn_permission(&self, check: &ConnCheck) -> bool {
1199        if check.pair.local.candidate_type == CandidateType::Relayed {
1200            let Some((_id, client)) = self.turn_client_by_allocated_address(
1201                check.pair.local.transport_type,
1202                check.pair.local.base_address,
1203            ) else {
1204                return false;
1205            };
1206            if client
1207                .permissions(
1208                    check.pair.local.transport_type,
1209                    check.pair.local.base_address,
1210                )
1211                .all(|permission| permission != check.pair.remote.address.ip())
1212            {
1213                return false;
1214            }
1215        }
1216        true
1217    }
1218
1219    fn foundation_not_waiting_in_progress(&self, foundation: &str) -> bool {
1220        for check in self
1221            .pairs
1222            .iter()
1223            .filter(|check| check.pair.foundation() == foundation)
1224        {
1225            if !self.check_has_turn_permission(check) {
1226                return false;
1227            }
1228            let state = check.state();
1229            if [CandidatePairState::InProgress, CandidatePairState::Waiting].contains(&state) {
1230                return false;
1231            }
1232        }
1233        true
1234    }
1235
1236    /// The list of local candidates currently configured for this checklist
1237    pub fn local_candidates(&self) -> impl Iterator<Item = &'_ Candidate> + '_ {
1238        self.local_candidates.iter().map(|local| &local.candidate)
1239    }
1240
1241    /// The list of remote candidates currently configured for this checklist
1242    pub fn remote_candidates(&self) -> &[Candidate] {
1243        &self.remote_candidates
1244    }
1245
1246    #[tracing::instrument(
1247        name = "set_checklist_state",
1248        level = "debug",
1249        skip(self),
1250        fields(
1251            self.checklist_id,
1252        )
1253    )]
1254    fn set_state(&mut self, state: CheckListState) {
1255        if self.state != state {
1256            trace!(old_state = ?self.state, new_state = ?state, "changing state");
1257            self.state = state;
1258        }
1259    }
1260
1261    #[tracing::instrument(
1262        level = "debug",
1263        skip(self),
1264        fields(
1265            self.checklist_id
1266        )
1267    )]
1268    fn find_remote_candidate(
1269        &self,
1270        component_id: usize,
1271        ttype: TransportType,
1272        addr: SocketAddr,
1273    ) -> Option<Candidate> {
1274        self.remote_candidates
1275            .iter()
1276            .find(|&remote| {
1277                remote.component_id == component_id
1278                    && remote.transport_type == ttype
1279                    && remote.address == addr
1280            })
1281            .cloned()
1282    }
1283
1284    fn check_by_id(&self, id: ConnCheckId) -> Option<&ConnCheck> {
1285        self.pairs.iter().find(|check| check.conncheck_id == id)
1286    }
1287
1288    fn mut_check_by_id(&mut self, id: ConnCheckId) -> Option<&mut ConnCheck> {
1289        self.pairs.iter_mut().find(|check| check.conncheck_id == id)
1290    }
1291
1292    #[tracing::instrument(
1293        level = "debug",
1294        skip(self, check),
1295        fields(
1296            self.checklist_id,
1297            check.conncheck_id
1298        )
1299    )]
1300    fn add_triggered(&mut self, check: &ConnCheck) {
1301        if let Some(idx) = self.triggered.iter().position(|&existing| {
1302            self.check_by_id(existing).is_some_and(|existing| {
1303                candidate_pair_is_same_connection(&existing.pair, &check.pair)
1304            })
1305        }) {
1306            let triggered = self.check_by_id(self.triggered[idx]).unwrap();
1307            // a nominating check trumps not nominating.  Otherwise, if the peers are delay sync,
1308            // then the non-nominating trigerred check may override the nomination process for a
1309            // long time and delay the connection process
1310            if check.nominate() && !triggered.nominate()
1311                || triggered.state() == CandidatePairState::Failed
1312            {
1313                let existing = self.triggered.remove(idx).unwrap();
1314                debug!("removing existing triggered {}", existing);
1315            } else {
1316                debug!("not adding duplicate triggered check");
1317                return;
1318            }
1319        }
1320        debug!("adding triggered check {}", check.conncheck_id);
1321        self.triggered.push_front(check.conncheck_id)
1322    }
1323
1324    fn foundation_has_check_state(&self, foundation: &str, state: CandidatePairState) -> bool {
1325        self.pairs
1326            .iter()
1327            .any(|check| check.pair.foundation() == foundation && check.state() == state)
1328    }
1329
1330    fn thawn_foundations(&mut self) -> Vec<String> {
1331        // XXX: cache this?
1332        let mut thawn_foundations = Vec::new();
1333        for check in self.pairs.iter() {
1334            if !thawn_foundations
1335                .iter()
1336                .any(|foundation| check.pair.foundation() == *foundation)
1337            {
1338                thawn_foundations.push(check.pair.foundation().clone());
1339            }
1340        }
1341        thawn_foundations
1342    }
1343
1344    #[tracing::instrument(
1345        level = "debug",
1346        skip(self),
1347        fields(
1348            checklist_id = self.checklist_id
1349        )
1350    )]
1351    fn generate_checks(&mut self) {
1352        let mut checks = Vec::new();
1353        // Trickle ICE mandates that we only check redundancy with Frozen or Waiting pairs.  This
1354        // is compatible with non-trickle-ICE as checks start off in the frozen state until the
1355        // initial thaw.
1356        let mut pairs: Vec<_> = self
1357            .pairs
1358            .iter()
1359            .filter_map(|check| {
1360                if matches!(
1361                    check.state(),
1362                    CandidatePairState::Waiting | CandidatePairState::Frozen
1363                ) {
1364                    Some(check.pair.clone())
1365                } else {
1366                    None
1367                }
1368            })
1369            .collect();
1370        let mut redundant_pairs = Vec::new();
1371        let mut turn_checks = Vec::new();
1372
1373        for local in self.local_candidates.iter() {
1374            let turn_client_id = {
1375                trace!("turn clients: {:?}", self.turn_clients);
1376                let turn_client_id = self
1377                    .turn_client_by_allocated_address(
1378                        local.candidate.transport_type,
1379                        local.candidate.base_address,
1380                    )
1381                    .map(|(id, _client)| id);
1382                if local.candidate.candidate_type == CandidateType::Relayed
1383                    && turn_client_id.is_none()
1384                {
1385                    warn!("No configured TURN client for candidate: {local:?}, ignoring");
1386                    continue;
1387                }
1388                turn_client_id
1389            };
1390            for remote in self.remote_candidates.iter() {
1391                if candidate_can_pair_with(&local.candidate, remote) {
1392                    let pair = CandidatePair::new(local.candidate.clone(), remote.clone());
1393                    let component_id = self
1394                        .component_ids
1395                        .iter()
1396                        .find(|(id, _state)| id == &local.candidate.component_id)
1397                        .unwrap_or_else(|| {
1398                            panic!(
1399                                "No component {} for local candidate",
1400                                local.candidate.component_id
1401                            )
1402                        });
1403
1404                    if let Some(redundant_pair) = pair.redundant_with(pairs.iter()) {
1405                        if redundant_pair.remote.candidate_type == CandidateType::PeerReflexive {
1406                            match local.variant {
1407                                LocalCandidateVariant::Agent(ref agent_id) => {
1408                                    redundant_pairs.push((
1409                                        redundant_pair.clone(),
1410                                        pair,
1411                                        *agent_id,
1412                                        component_id,
1413                                    ));
1414                                }
1415                                LocalCandidateVariant::TcpActive
1416                                | LocalCandidateVariant::TcpListener => {}
1417                            }
1418                        } else {
1419                            //trace!("not adding redundant pair {:?}", pair);
1420                        }
1421                    } else {
1422                        if let Some(turn_client_id) = turn_client_id {
1423                            turn_checks.push((turn_client_id, pair.clone()));
1424                        }
1425                        trace!("constructed pair: {pair:?}");
1426                        pairs.push(pair.clone());
1427                        match local.variant {
1428                            LocalCandidateVariant::Agent(ref agent_id) => {
1429                                checks.push(ConnCheck::new(
1430                                    self.checklist_id,
1431                                    pair.clone(),
1432                                    *agent_id,
1433                                    false,
1434                                    self.controlling,
1435                                    self.remote_credentials.clone(),
1436                                ));
1437                            }
1438                            LocalCandidateVariant::TcpActive => {
1439                                checks.push(ConnCheck::new_tcp(
1440                                    self.checklist_id,
1441                                    pair.clone(),
1442                                    false,
1443                                    self.controlling,
1444                                    self.remote_credentials.clone(),
1445                                ));
1446                            }
1447                            LocalCandidateVariant::TcpListener => (), // FIXME need something here?
1448                        }
1449                        //debug!("generated pair {:?}", pair);
1450                    }
1451                }
1452            }
1453        }
1454        /*
1455        for (peer_reflexive_pair, pair, local, component) in redundant_pairs {
1456            // try to replace the existing check with a non-peer reflexive version
1457            if let Some(check) = self.take_matching_check(&peer_reflexive_pair) {
1458                if !matches!(check.state(), CandidatePairState::InProgress) {
1459                    check.cancel();
1460                    match local {
1461                        LocalCandidateVariant::StunAgent(agent) => checks.push(Arc::new(ConnCheck::new(pair, agent.stun_agent, false))),
1462                        LocalCandidateVariant::TcpListen(_tcp) => checks.push(Arc::new(ConnCheck::new_tcp(pair, false, weak_inner.clone(), component))),
1463                        LocalCandidateVariant::TcpActive => (),
1464                    }
1465                } else {
1466                    self.add_check_if_not_duplicate(check);
1467                }
1468            }
1469        }*/
1470
1471        for (turn_id, pair) in turn_checks {
1472            debug!(
1473                "Adding turn permission for {} using TURN allocation {} {}",
1474                pair.remote.address, pair.local.transport_type, pair.local.base_address
1475            );
1476            self.pending_turn_permissions.push_front((
1477                turn_id,
1478                pair.local.transport_type,
1479                pair.remote.address.ip(),
1480            ));
1481        }
1482
1483        let mut thawn_foundations = if self.trickle_ice {
1484            self.thawn_foundations()
1485        } else {
1486            Vec::new()
1487        };
1488        for mut check in checks {
1489            if self.trickle_ice {
1490                // for the trickle-ICE case, if the foundation does not already have a waiting check,
1491                // then we use this check as the first waiting check
1492                // RFC 8838 Section 12 Rule 1, 2, and 3
1493                if !thawn_foundations
1494                    .iter()
1495                    .any(|foundation| check.pair.foundation() == *foundation)
1496                    && self.check_has_turn_permission(&check)
1497                {
1498                    check.set_state(CandidatePairState::Waiting);
1499                    thawn_foundations.push(check.pair.foundation());
1500                } else if self.foundation_has_check_state(
1501                    &check.pair.foundation(),
1502                    CandidatePairState::Succeeded,
1503                ) {
1504                    check.set_state(CandidatePairState::Waiting);
1505                }
1506            }
1507            self.add_check_if_not_duplicate(check);
1508        }
1509    }
1510
1511    fn check_is_equal(check: &ConnCheck, pair: &CandidatePair, nominate: Nominate) -> bool {
1512        candidate_is_same_connection(&check.pair.local, &pair.local)
1513            && candidate_is_same_connection(&check.pair.remote, &pair.remote)
1514            && nominate.eq(&check.nominate)
1515    }
1516
1517    #[tracing::instrument(
1518        level = "trace",
1519        skip(self, pair),
1520        fields(
1521            ttype = ?pair.local.transport_type,
1522            local.base_address = ?pair.local.base_address,
1523            remote.address = ?pair.remote.address,
1524            local.ctype = ?pair.local.candidate_type,
1525            remote.ctype = ?pair.remote.candidate_type,
1526            foundation = %pair.foundation(),
1527        )
1528    )]
1529    fn matching_check_idx(&self, pair: &CandidatePair, nominate: Nominate) -> Option<usize> {
1530        let mut best_index = None;
1531        for (idx, potential) in self
1532            .pairs
1533            .iter()
1534            .enumerate()
1535            .filter(|(_idx, check)| Self::check_is_equal(check, pair, nominate))
1536        {
1537            let best = best_index.get_or_insert(idx);
1538            if *best != idx {
1539                let best_check = &self.pairs[*best];
1540                trace!("comparing current best {best_check:?} with {potential:?}");
1541                if potential.nominate && !best_check.nominate {
1542                    trace!("better because of nomination");
1543                    *best = idx;
1544                    continue;
1545                }
1546                if potential.state.cmp_progression(&best_check.state).is_gt() {
1547                    trace!("better because of state");
1548                    *best = idx;
1549                    continue;
1550                }
1551            }
1552        }
1553        if let Some(idx) = best_index {
1554            trace!("found check {}", self.pairs[idx].conncheck_id);
1555            Some(idx)
1556        } else {
1557            trace!("could not find check");
1558            None
1559        }
1560    }
1561
1562    fn take_matching_check(
1563        &mut self,
1564        pair: &CandidatePair,
1565        nominate: Nominate,
1566    ) -> Option<ConnCheck> {
1567        self.matching_check_idx(pair, nominate)
1568            .and_then(|idx| self.pairs.remove(idx))
1569    }
1570
1571    fn matching_check(&self, pair: &CandidatePair, nominate: Nominate) -> Option<&ConnCheck> {
1572        self.matching_check_idx(pair, nominate)
1573            .map(|idx| &self.pairs[idx])
1574    }
1575
1576    fn add_check_if_not_duplicate(&mut self, check: ConnCheck) -> bool {
1577        if let Some(idx) = self
1578            .pairs
1579            .iter()
1580            .position(|existing| candidate_pair_is_same_connection(&existing.pair, &check.pair))
1581        {
1582            // a nominating check trumps not nominating.  Otherwise, if the peers are delay sync,
1583            // then the non-nominating trigerred check may override the nomination process for a
1584            // long time and delay the connection process
1585            if check.nominate() && !self.pairs[idx].nominate()
1586                || self.pairs[idx].state() == CandidatePairState::Failed
1587            {
1588                let existing = self.pairs.remove(idx).unwrap();
1589                debug!("removing existing check {:?}", existing);
1590            } else {
1591                debug!("not adding duplicate check");
1592                return false;
1593            }
1594        }
1595
1596        self.add_check(check);
1597        true
1598    }
1599
1600    fn add_check(&mut self, check: ConnCheck) {
1601        trace!("adding check {}", check.conncheck_id);
1602
1603        let idx = self
1604            .pairs
1605            .binary_search_by(|existing| {
1606                existing
1607                    .pair
1608                    .priority(self.controlling)
1609                    .cmp(&check.pair.priority(self.controlling))
1610                    .reverse()
1611            })
1612            .unwrap_or_else(|x| x);
1613        self.pairs.insert(idx, check);
1614        self.dump_check_state();
1615    }
1616
1617    fn set_controlling(&mut self, controlling: bool) {
1618        self.controlling = controlling;
1619        // changing the controlling (and therefore priority) requires resorting
1620        self.sort_pairs();
1621    }
1622
1623    fn sort_pairs(&mut self) {
1624        self.pairs.make_contiguous().sort_by(|a, b| {
1625            a.pair
1626                .priority(self.controlling)
1627                .cmp(&b.pair.priority(self.controlling))
1628                .reverse()
1629        })
1630    }
1631
1632    #[tracing::instrument(
1633        level = "debug",
1634        skip(self, pair),
1635        fields(
1636            checklist_id = self.checklist_id,
1637        )
1638    )]
1639    fn add_valid(&mut self, conncheck_id: ConnCheckId, pair: &CandidatePair) {
1640        if pair.local.transport_type == TransportType::Tcp
1641            && pair.local.tcp_type == Some(TcpType::Passive)
1642            && pair.local.address.port() == 9
1643        {
1644            warn!("not adding local passive tcp candidate without a valid port");
1645        }
1646        trace!(
1647            ttype = ?pair.local.transport_type,
1648            local.address = ?pair.local.address,
1649            remote.address = ?pair.remote.address,
1650            local.ctype = ?pair.local.candidate_type,
1651            remote.ctype = ?pair.remote.candidate_type,
1652            foundation = %pair.foundation(),
1653            "adding valid {conncheck_id}"
1654        );
1655        self.valid.push(conncheck_id);
1656    }
1657
1658    #[tracing::instrument(
1659        level = "debug",
1660        skip(self),
1661        fields(
1662            checklist_id = self.checklist_id
1663        )
1664    )]
1665    fn remove_valid(&mut self, pair: &CandidatePair) {
1666        let valid_to_remove = self
1667            .valid
1668            .iter()
1669            .filter(|&check_id| {
1670                let Some(check) = self.check_by_id(*check_id) else {
1671                    return true;
1672                };
1673                candidate_pair_is_same_connection(&check.pair, pair)
1674            })
1675            .cloned()
1676            .collect::<Vec<_>>();
1677        self.valid
1678            .retain(|check_id| !valid_to_remove.contains(check_id));
1679    }
1680
1681    #[tracing::instrument(
1682        level = "debug",
1683        skip(self),
1684        fields(checklist.id = self.checklist_id)
1685    )]
1686    fn check_for_failure(&mut self) {
1687        if self.state == CheckListState::Completed {
1688            return;
1689        }
1690        if self.local_end_of_candidates && self.remote_end_of_candidates {
1691            debug!("all candidates have arrived");
1692            let mut any_not_failed = false;
1693            for (component_id, state) in self.component_ids.iter_mut() {
1694                if self.pairs.iter().any(|check| {
1695                    check.pair.local.component_id == *component_id
1696                        && check.state() != CandidatePairState::Failed
1697                }) {
1698                    any_not_failed = true;
1699                    trace!("component {component_id} has any non-failed check");
1700                } else if *state != ComponentConnectionState::Failed {
1701                    *state = ComponentConnectionState::Failed;
1702                    self.events.push_front(ConnCheckEvent::ComponentState(
1703                        *component_id,
1704                        ComponentConnectionState::Failed,
1705                    ));
1706                }
1707            }
1708            if !any_not_failed {
1709                self.set_state(CheckListState::Failed);
1710            }
1711        }
1712    }
1713
1714    #[tracing::instrument(
1715        level = "debug",
1716        skip(self, pair),
1717        fields(component.id = pair.local.component_id)
1718    )]
1719    fn nominated_pair(&mut self, pair: &CandidatePair) {
1720        self.dump_check_state();
1721        if let Some(idx) = self.valid.iter().position(|&check_id| {
1722            self.check_by_id(check_id).is_some_and(|check| {
1723                check.nominate && candidate_pair_is_same_connection(&check.pair, pair)
1724            })
1725        }) {
1726            info!(
1727                ttype = ?pair.local.transport_type,
1728                local.address = ?pair.local.address,
1729                remote.address = ?pair.remote.address,
1730                local.ctype = ?pair.local.candidate_type,
1731                remote.ctype = ?pair.remote.candidate_type,
1732                foundation = %pair.foundation(),
1733                "nominated"
1734            );
1735            self.nominated.push(self.valid.remove(idx));
1736            let component_id = self.component_ids.iter().find_map(|(id, _state)| {
1737                if *id == pair.local.component_id {
1738                    Some(*id)
1739                } else {
1740                    None
1741                }
1742            });
1743            // o Once a candidate pair for a component of a data stream has been
1744            //   nominated, and the state of the checklist associated with the data
1745            //   stream is Running, the ICE agent MUST remove all candidate pairs
1746            //   for the same component from the checklist and from the triggered-
1747            //   check queue.  If the state of a pair is In-Progress, the agent
1748            //   cancels the In-Progress transaction.  Cancellation means that the
1749            //   agent will not retransmit the Binding requests associated with the
1750            //   connectivity-check transaction, will not treat the lack of
1751            //   response to be a failure, but will wait the duration of the
1752            //   transaction timeout for a response.
1753            let triggered_to_remove = self
1754                .triggered
1755                .iter()
1756                .filter(|&check_id| {
1757                    let Some(check) = self.check_by_id(*check_id) else {
1758                        return true;
1759                    };
1760                    check.pair.local.component_id == pair.local.component_id
1761                })
1762                .cloned()
1763                .collect::<Vec<_>>();
1764            self.triggered
1765                .retain(|check_id| !triggered_to_remove.contains(check_id));
1766            let nominated_ids = self.nominated.clone();
1767            self.pairs.retain(|check| {
1768                if nominated_ids.contains(&check.conncheck_id) {
1769                    true
1770                } else {
1771                    check.pair.local.component_id != pair.local.component_id
1772                }
1773            });
1774            // XXX: do we also need to clear self.valid?
1775            // o Once candidate pairs for each component of a data stream have been
1776            //   nominated, and the state of the checklist associated with the data
1777            //   stream is Running, the ICE agent sets the state of the checklist
1778            //   to Completed.
1779            let all_nominated = self.component_ids.iter().all(|(component_id, _state)| {
1780                self.nominated
1781                    .iter()
1782                    .filter_map(|&check_id| self.check_by_id(check_id))
1783                    .any(|check| check.pair.local.component_id == *component_id)
1784            });
1785            if all_nominated {
1786                // ... Once an ICE agent sets the
1787                // state of the checklist to Completed (when there is a nominated pair
1788                // for each component of the data stream), that pair becomes the
1789                // selected pair for that agent and is used for sending and receiving
1790                // data for that component of the data stream.
1791                info!(
1792                    "all {} component/s nominated, setting selected pair/s",
1793                    self.component_ids.len()
1794                );
1795                self.nominated.clone().iter().fold(
1796                    Vec::new(),
1797                    |mut component_ids_selected, &check_id| {
1798                        let Some(check) = self.check_by_id(check_id) else {
1799                            return component_ids_selected;
1800                        };
1801                        let check_component_id = check.pair.local.component_id;
1802                        // Only nominate one valid candidatePair
1803                        if component_ids_selected.contains(&check_component_id) {
1804                            return component_ids_selected;
1805                        }
1806                        if let Some(component_id) = component_id {
1807                            let agent_id = check.agent_id().unwrap();
1808                            let turn = {
1809                                let ret = self
1810                                    .turn_client_by_allocated_address(
1811                                        check.pair.local.transport_type,
1812                                        check.pair.local.base_address,
1813                                    )
1814                                    .map(|(_turn_id, client)| SelectedTurn {
1815                                        transport: client.transport(),
1816                                        local_addr: client.local_addr(),
1817                                        remote_addr: client.remote_addr(),
1818                                    });
1819                                if check.pair.local.candidate_type == CandidateType::Relayed {
1820                                    trace!("turn clients: {:?}", self.turn_clients);
1821                                    debug_assert!(ret.is_some());
1822                                }
1823                                ret
1824                            };
1825                            self.events.push_front(ConnCheckEvent::SelectedPair(
1826                                component_id,
1827                                Box::new(SelectedPair::new(pair.clone(), agent_id, turn)),
1828                            ));
1829                            debug!("trying to signal component {:?}", component_id);
1830                            self.events.push_front(ConnCheckEvent::ComponentState(
1831                                component_id,
1832                                ComponentConnectionState::Connected,
1833                            ));
1834                        }
1835                        component_ids_selected.push(check_component_id);
1836                        component_ids_selected
1837                    },
1838                );
1839                self.set_state(CheckListState::Completed);
1840            }
1841        } else {
1842            warn!("unknown nomination for pair {pair:?}");
1843        }
1844        self.dump_check_state();
1845    }
1846
1847    fn try_nominate(&mut self) {
1848        let retriggered: Vec<_> = self
1849            .component_ids
1850            .iter()
1851            .map(|(component_id, _state)| {
1852                let nominated = self.pairs.iter().find(|check| check.nominate());
1853                nominated.or({
1854                    let mut valid: Vec<_> = self
1855                        .valid
1856                        .iter()
1857                        .filter_map(|&check_id| self.check_by_id(check_id))
1858                        .filter(|check| {
1859                            check.pair.local.component_id == *component_id
1860                                && (check.pair.local.transport_type != TransportType::Tcp
1861                                    || check.pair.local.address.port() != 9)
1862                        })
1863                        .collect();
1864                    valid.sort_by(|check1, check2| {
1865                        check1
1866                            .pair
1867                            .priority(true /* if we are nominating, we are controlling */)
1868                            .cmp(&check2.pair.priority(true))
1869                    });
1870                    // FIXME: Nominate when there are two valid candidates
1871                    // what if there is only ever one valid?
1872                    if !valid.is_empty() {
1873                        Some(valid[0])
1874                    } else {
1875                        None
1876                    }
1877                })
1878            })
1879            .collect();
1880        trace!("retriggered {:?}", retriggered);
1881        // need to wait until all component have a valid pair before we send nominations
1882        if retriggered.iter().all(|pair| pair.is_some()) {
1883            self.dump_check_state();
1884            info!("all components have successful connchecks");
1885            let new_checks = retriggered
1886                .iter()
1887                .filter_map(|check| {
1888                    let check = check.as_ref().unwrap(); // checked earlier
1889                    if check.nominate() {
1890                        trace!(
1891                            "already have nominate check for component {}",
1892                            check.pair.local.component_id
1893                        );
1894                        None
1895                    } else {
1896                        let agent_id = check.agent_id().unwrap();
1897                        let mut check = ConnCheck::new(
1898                            self.checklist_id,
1899                            check.pair.clone(),
1900                            agent_id,
1901                            true,
1902                            self.controlling,
1903                            self.remote_credentials.clone(),
1904                        );
1905                        check.set_state(CandidatePairState::Waiting);
1906                        debug!("attempting nomination with check {}", check.conncheck_id);
1907                        Some(check)
1908                    }
1909                })
1910                .collect::<Vec<_>>();
1911            for check in new_checks {
1912                self.add_triggered(&check);
1913                self.add_check(check);
1914            }
1915        }
1916    }
1917
1918    #[tracing::instrument(level = "trace", skip(self))]
1919    fn dump_check_state(&self) {
1920        let mut s = alloc::format!("checklist {}", self.checklist_id);
1921        for pair in self.pairs.iter() {
1922            use core::fmt::Write as _;
1923            let _ = write!(
1924                &mut s,
1925                "\nID:{id} foundation:{foundation} state:{state} nom:{nominate} con:{controlling} priority:{local_pri},{remote_pri} trans:{transport} local:{local_cand_type} {local_addr} remote:{remote_cand_type} {remote_addr}",
1926                id = format_args!("{:<3}", pair.conncheck_id),
1927                foundation = format_args!("{:10}", pair.pair.foundation()),
1928                state = format_args!("{:10}", pair.state()),
1929                nominate = format_args!("{:5}", pair.nominate()),
1930                controlling = format_args!("{:5}", pair.controlling),
1931                local_pri = format_args!("{:10}", pair.pair.local.priority),
1932                remote_pri = format_args!("{:10}", pair.pair.remote.priority),
1933                transport = format_args!("{:4}", pair.pair.local.transport_type),
1934                local_cand_type = format_args!("{:5}", pair.pair.local.candidate_type),
1935                local_addr = format_args!("{:32}", pair.pair.local.base_address),
1936                remote_cand_type = format_args!("{:5}", pair.pair.remote.candidate_type),
1937                remote_addr = format_args!("{:32}", pair.pair.remote.address)
1938            );
1939        }
1940        debug!("{}", s);
1941    }
1942
1943    fn check_response_failure(&mut self, conncheck_id: ConnCheckId) {
1944        let conncheck = self.mut_check_by_id(conncheck_id).unwrap();
1945        warn!("conncheck failure for id {conncheck_id}");
1946        conncheck.set_state(CandidatePairState::Failed);
1947        let pair = conncheck.pair.clone();
1948        if conncheck.nominate() {
1949            self.set_state(CheckListState::Failed);
1950        }
1951        self.remove_valid(&pair);
1952        if self.local_end_of_candidates && self.remote_end_of_candidates {
1953            self.check_for_failure();
1954        }
1955    }
1956
1957    fn mut_check_from_stun_response(
1958        &mut self,
1959        transaction: TransactionId,
1960        _from: SocketAddr,
1961    ) -> Option<&mut ConnCheck> {
1962        self.pairs.iter_mut().find_map(|check| {
1963            check
1964                .stun_request
1965                .filter(|&request| request == transaction)
1966                .map(|_request| check)
1967        })
1968    }
1969
1970    fn find_local_candidate(
1971        &self,
1972        transport: TransportType,
1973        addr: SocketAddr,
1974    ) -> Option<Candidate> {
1975        let f = |candidate: &Candidate| -> bool {
1976            candidate.transport_type == transport && candidate.base_address == addr
1977        };
1978        self.local_candidates
1979            .iter()
1980            .find(|cand| f(&cand.candidate))
1981            .map(|c| c.candidate.clone())
1982            .or_else(|| {
1983                if transport == TransportType::Tcp {
1984                    self.pairs
1985                        .iter()
1986                        .find(|&check| f(&check.pair.local))
1987                        .map(|c| c.pair.local.clone())
1988                } else {
1989                    None
1990                }
1991            })
1992    }
1993
1994    fn check_cancel(&mut self, check_id: ConnCheckId) {
1995        let Some(check) = self.mut_check_by_id(check_id) else {
1996            return;
1997        };
1998        let Some(agent_id) = check.agent_id() else {
1999            return;
2000        };
2001        let Some(transaction_id) = check.stun_request else {
2002            return;
2003        };
2004        debug!(conncheck.id = *check_id, "cancelling conncheck");
2005        check.set_state(CandidatePairState::Failed);
2006        let Some(agent) = self.mut_agent_by_id(agent_id) else {
2007            return;
2008        };
2009        let Some(mut request) = agent.mut_request_transaction(transaction_id) else {
2010            return;
2011        };
2012        request.cancel();
2013    }
2014
2015    fn check_cancel_retransmissions(&mut self, check_id: ConnCheckId) {
2016        let Some(check) = self.mut_check_by_id(check_id) else {
2017            return;
2018        };
2019        let Some(agent_id) = check.agent_id() else {
2020            return;
2021        };
2022        let Some(transaction_id) = check.stun_request else {
2023            return;
2024        };
2025        debug!(
2026            conncheck.id = *check_id,
2027            "cancelling conncheck retransmissions"
2028        );
2029        let Some(agent) = self.mut_agent_by_id(agent_id) else {
2030            return;
2031        };
2032        let Some(mut request) = agent.mut_request_transaction(transaction_id) else {
2033            return;
2034        };
2035        request.cancel_retransmissions();
2036    }
2037
2038    fn prune_checks_with<F: FnMut(&ConnCheck) -> bool>(
2039        &mut self,
2040        mut precondition: F,
2041    ) -> Vec<ConnCheck> {
2042        let mut i = 0;
2043        let mut ret = Vec::new();
2044        while let Some(check) = self.pairs.get(i) {
2045            if precondition(check) {
2046                ret.push(self.pairs.remove(i).unwrap());
2047            } else {
2048                i += 1;
2049            }
2050        }
2051        ret
2052    }
2053
2054    fn close(&mut self) {
2055        self.state = CheckListState::Failed;
2056        self.component_ids.clear();
2057        self.local_candidates.clear();
2058        self.remote_candidates.clear();
2059        self.triggered.clear();
2060        self.pairs.clear();
2061        self.valid.clear();
2062        self.nominated.clear();
2063        self.pending_turn_permissions.clear();
2064        self.agents.clear();
2065    }
2066}
2067
2068/// A builder for a [`ConnCheckListSet`]
2069pub struct ConnCheckListSetBuilder {
2070    tie_breaker: u64,
2071    controlling: bool,
2072    trickle_ice: bool,
2073    timing_advance: Duration,
2074    rto: Option<RequestRto>,
2075}
2076
2077impl ConnCheckListSetBuilder {
2078    fn new(tie_breaker: u64, controlling: bool) -> Self {
2079        Self {
2080            tie_breaker,
2081            controlling,
2082            trickle_ice: false,
2083            timing_advance: DEFAULT_MINIMUM_SET_TICK,
2084            rto: None,
2085        }
2086    }
2087
2088    /// Whether ICE candidate will be trickled
2089    pub fn trickle_ice(mut self, trickle_ice: bool) -> Self {
2090        self.trickle_ice = trickle_ice;
2091        self
2092    }
2093
2094    /// Set the minimum amount of time between subsequent STUN requests sent.
2095    ///
2096    /// This is known as the Ta value in the ICE specification.
2097    ///
2098    /// The default value is 50ms.
2099    pub fn timing_advance(mut self, ta: Duration) -> Self {
2100        self.timing_advance = ta;
2101        self
2102    }
2103
2104    /// Build the [`ConnCheckListSet`]
2105    pub fn build(self) -> ConnCheckListSet {
2106        ConnCheckListSet {
2107            checklists: Default::default(),
2108            tie_breaker: self.tie_breaker,
2109            controlling: self.controlling,
2110            trickle_ice: self.trickle_ice,
2111            checklist_i: 0,
2112            last_send_time: None,
2113            pending_messages: Default::default(),
2114            pending_transmits: Default::default(),
2115            pending_remove_sockets: Default::default(),
2116            completed: false,
2117            closed: false,
2118            timing_advance: self.timing_advance,
2119            rto: self.rto,
2120        }
2121    }
2122}
2123
2124/// The minimum amount of time between iterations of a [`ConnCheckListSet`]
2125pub const DEFAULT_MINIMUM_SET_TICK: Duration = Duration::from_millis(50);
2126/// Minimum Ta as specified in RFC8445
2127pub const MIN_TIMING_ADVANCE: Duration = Duration::from_millis(5);
2128
2129/// A set of [`ConnCheckList`]s within an ICE Agent.
2130#[derive(Debug)]
2131pub struct ConnCheckListSet {
2132    checklists: Vec<ConnCheckList>,
2133    tie_breaker: u64,
2134    controlling: bool,
2135    trickle_ice: bool,
2136    checklist_i: usize,
2137    last_send_time: Option<Instant>,
2138    pending_messages: VecDeque<CheckListSetPendingMessage>,
2139    pending_transmits: VecDeque<CheckListSetTransmit>,
2140    pending_remove_sockets: VecDeque<CheckListSetSocket>,
2141    completed: bool,
2142    closed: bool,
2143    timing_advance: Duration,
2144    rto: Option<RequestRto>,
2145}
2146
2147impl ConnCheckListSet {
2148    // TODO: add/remove a stream after start
2149    // TODO: cancel when agent is stopped
2150    /// Create a [`ConnCheckListSetBuilder`]
2151    pub fn builder(tie_breaker: u64, controlling: bool) -> ConnCheckListSetBuilder {
2152        ConnCheckListSetBuilder::new(tie_breaker, controlling)
2153    }
2154
2155    /// Construct a new [`ConnCheckList`] and return its ID.
2156    pub fn new_list(&mut self) -> usize {
2157        let checklist_id = CONN_CHECK_LIST_COUNT.fetch_add(1, Ordering::SeqCst);
2158        let ret = ConnCheckList::new(checklist_id, self.controlling(), self.trickle_ice);
2159        self.checklists.push(ret);
2160        checklist_id
2161    }
2162
2163    /// Get a mutable reference to a [`ConnCheckList`] by ID
2164    pub fn mut_list(&mut self, id: usize) -> Option<&mut ConnCheckList> {
2165        self.checklists.iter_mut().find(|cl| cl.checklist_id == id)
2166    }
2167
2168    /// Get a reference to a [`ConnCheckList`] by ID
2169    pub fn list(&self, id: usize) -> Option<&ConnCheckList> {
2170        self.checklists.iter().find(|cl| cl.checklist_id == id)
2171    }
2172
2173    /// Whether the set is in the controlling mode.  This may change during the ICE negotiation
2174    /// process.
2175    pub fn controlling(&self) -> bool {
2176        self.controlling
2177    }
2178
2179    /// Set the minimum amount of time between subsequent STUN requests sent.
2180    ///
2181    /// This is known as the Ta value in the ICE specification.
2182    ///
2183    /// The default value is 50ms.
2184    pub fn timing_advance(&self) -> Duration {
2185        self.timing_advance
2186    }
2187
2188    /// Set the minimum amount of time between subsequent STUN requests sent.
2189    ///
2190    /// This is known as the Ta value in the ICE specification.
2191    ///
2192    /// The default value is 50ms.
2193    pub fn set_timing_advance(&mut self, ta: Duration) {
2194        self.timing_advance = ta.max(MIN_TIMING_ADVANCE);
2195    }
2196
2197    pub fn set_request_retransmits(&mut self, rto: RequestRto) {
2198        for checklist in self.checklists.iter_mut() {
2199            let requests = checklist
2200                .pairs
2201                .iter()
2202                .filter_map(|check| check.agent_id().zip(check.stun_request))
2203                .collect::<Vec<_>>();
2204            for (agent_id, request_id) in requests {
2205                let Some(agent) = checklist.mut_agent_by_id(agent_id) else {
2206                    continue;
2207                };
2208                let Some(mut transaction) = agent.mut_request_transaction(request_id) else {
2209                    continue;
2210                };
2211                transaction.configure_timeout_with_max(
2212                    rto.initial,
2213                    rto.retransmits,
2214                    rto.final_retransmit_timeout,
2215                    rto.max,
2216                );
2217            }
2218        }
2219        self.rto = Some(rto);
2220    }
2221
2222    fn handle_stun<T: AsRef<[u8]>>(
2223        &mut self,
2224        checklist_i: usize,
2225        msg: Message<'_>,
2226        transmit: &Transmit<T>,
2227        agent_id: StunAgentId,
2228        turn_id: Option<(StunAgentId, SocketAddr)>,
2229    ) -> bool {
2230        debug!("received STUN message {msg}");
2231        if msg.is_response() {
2232            match self.checklists[checklist_i]
2233                .stun_auth_remote
2234                .validate_incoming_message(&msg)
2235            {
2236                Ok(Some(_algo)) => {
2237                    let Some(agent) = self.checklists[checklist_i].mut_agent_by_id(agent_id) else {
2238                        return false;
2239                    };
2240                    if !agent.handle_stun_message(&msg, transmit.from) {
2241                        return false;
2242                    }
2243                    self.handle_stun_response(checklist_i, &msg, transmit.from);
2244                    true
2245                }
2246                _ => false,
2247            }
2248        } else if msg.has_class(MessageClass::Request) {
2249            match self.checklists[checklist_i]
2250                .stun_auth_local
2251                .validate_incoming_message(&msg)
2252            {
2253                Ok(Some(_algo)) => {
2254                    if !msg.has_method(BINDING) {
2255                        return false;
2256                    }
2257                    let Some(local_cand) = self.checklists[checklist_i]
2258                        .find_local_candidate(transmit.transport, transmit.to)
2259                    else {
2260                        warn!("Could not find local candidate for incoming data");
2261                        trace!(
2262                            "local candidates: {:?}",
2263                            self.checklists[checklist_i].local_candidates
2264                        );
2265                        return false;
2266                    };
2267
2268                    let checklist_id = self.checklists[checklist_i].checklist_id;
2269                    let response = self.handle_binding_request(
2270                        checklist_i,
2271                        &local_cand,
2272                        agent_id,
2273                        &msg,
2274                        transmit.from,
2275                    );
2276                    self.pending_messages.push_back(CheckListSetPendingMessage {
2277                        checklist_id,
2278                        agent_id,
2279                        is_request: false,
2280                        msg: response.finish(),
2281                        to: transmit.from,
2282                        turn_id,
2283                    });
2284                    true
2285                }
2286                Ok(None) => {
2287                    warn!("incoming message failed did not have integrity");
2288                    let code = ErrorCode::builder(ErrorCode::UNAUTHORIZED).build().unwrap();
2289                    let mut response = Message::builder_error(&msg, MessageWriteVec::new());
2290                    response.add_attribute(&code).unwrap();
2291                    self.pending_messages.push_back(CheckListSetPendingMessage {
2292                        checklist_id: self.checklists[checklist_i].checklist_id,
2293                        agent_id,
2294                        is_request: false,
2295                        msg: response.finish(),
2296                        to: transmit.from,
2297                        turn_id,
2298                    });
2299                    true
2300                }
2301                Err(e) => {
2302                    warn!("incoming message failed integrity check: {e:?}");
2303                    let code = ErrorCode::builder(ErrorCode::UNAUTHORIZED).build().unwrap();
2304                    let mut response = Message::builder_error(&msg, MessageWriteVec::new());
2305                    response.add_attribute(&code).unwrap();
2306                    self.pending_messages.push_back(CheckListSetPendingMessage {
2307                        checklist_id: self.checklists[checklist_i].checklist_id,
2308                        agent_id,
2309                        is_request: false,
2310                        msg: response.finish(),
2311                        to: transmit.from,
2312                        turn_id,
2313                    });
2314                    true
2315                }
2316            }
2317        } else {
2318            false
2319        }
2320    }
2321
2322    #[tracing::instrument(
2323        name = "incoming_data_or_stun",
2324        level = "trace",
2325        skip(self, checklist_i, transmit)
2326        fields(
2327            transport = %transmit.transport,
2328            from = %transmit.from,
2329            to = %transmit.to,
2330        )
2331    )]
2332    fn incoming_data_or_stun<T: AsRef<[u8]> + core::fmt::Debug>(
2333        &mut self,
2334        checklist_i: usize,
2335        component_id: usize,
2336        transmit: Transmit<T>,
2337        turn_client_id: Option<(StunAgentId, SocketAddr)>,
2338    ) -> HandleRecvReply<T> {
2339        if !self.checklists[checklist_i].pending_recv.is_empty() {
2340            panic!("Previous data has not been complete handled yet");
2341        }
2342        let (agent_id, checklist_i) = self.checklists[checklist_i]
2343            .find_agent_for_5tuple(transmit.transport, transmit.to, transmit.from)
2344            .map(|agent| (agent.0, checklist_i))
2345            .unwrap_or_else(|| {
2346                // else look at all checklists
2347                self.checklists
2348                    .iter()
2349                    .find_map(|checklist| {
2350                        checklist
2351                            .find_agent_for_5tuple(transmit.transport, transmit.to, transmit.from)
2352                            .map(|agent| (agent.0, checklist.checklist_id))
2353                    })
2354                    .unwrap_or_else(|| {
2355                        let (agent_id, _agent_idx) = self.checklists[checklist_i]
2356                            .add_agent_for_5tuple(
2357                                transmit.transport,
2358                                transmit.to,
2359                                transmit.from,
2360                                None,
2361                            );
2362                        (agent_id, checklist_i)
2363                    })
2364            });
2365
2366        match transmit.transport {
2367            TransportType::Udp => match Message::from_bytes(transmit.data.as_ref()) {
2368                Ok(msg) => {
2369                    if self.handle_stun(checklist_i, msg, &transmit, agent_id, turn_client_id) {
2370                        return HandleRecvReply {
2371                            handled: true,
2372                            have_more_data: false,
2373                            data: None,
2374                        };
2375                    }
2376                }
2377                Err(_) => {
2378                    if let Some(agent) = self.checklists[checklist_i].agent_by_id(agent_id) {
2379                        if agent.is_validated_peer(transmit.from) {
2380                            return HandleRecvReply {
2381                                handled: false,
2382                                have_more_data: false,
2383                                data: Some(DataAndRange {
2384                                    range: 0..transmit.data.as_ref().len(),
2385                                    data: transmit.data,
2386                                }),
2387                            };
2388                        }
2389                    }
2390                }
2391            },
2392            TransportType::Tcp => {
2393                // TODO: can potentially return a subset of the original data if the tcp buffer
2394                // is empty and the incoming data contains at least one message.
2395                let mut tcp_buffer = self.checklists[checklist_i]
2396                    .tcp_buffers
2397                    .entry((transmit.to, transmit.from))
2398                    .or_default();
2399                tcp_buffer.push_data(transmit.data.as_ref());
2400
2401                let mut handled = false;
2402                let mut have_more_data = false;
2403                loop {
2404                    let Some(data) = tcp_buffer.pull_data() else {
2405                        break;
2406                    };
2407                    match Message::from_bytes(&data) {
2408                        Ok(msg) => {
2409                            if self.handle_stun(
2410                                checklist_i,
2411                                msg,
2412                                &transmit,
2413                                agent_id,
2414                                turn_client_id,
2415                            ) {
2416                                handled = true;
2417                            }
2418                        }
2419                        Err(_) => {
2420                            let checklist = &mut self.checklists[checklist_i];
2421                            if let Some(agent) = checklist.agent_by_id(agent_id) {
2422                                if agent.is_validated_peer(transmit.from) {
2423                                    have_more_data = true;
2424                                    checklist
2425                                        .pending_recv
2426                                        .push_back(PendingRecv { component_id, data });
2427                                }
2428                            }
2429                        }
2430                    }
2431                    tcp_buffer = self.checklists[checklist_i]
2432                        .tcp_buffers
2433                        .get_mut(&(transmit.to, transmit.from))
2434                        .unwrap();
2435                }
2436                return HandleRecvReply {
2437                    handled,
2438                    have_more_data,
2439                    data: None,
2440                };
2441            }
2442        }
2443        HandleRecvReply {
2444            handled: false,
2445            have_more_data: false,
2446            data: None,
2447        }
2448    }
2449
2450    /// Provide received data to handle.  The returned values indicate what to do with the data.
2451    ///
2452    /// If [`HandleRecvReply::Handled`] is returned, then [`ConnCheckListSet::poll`] should be
2453    /// called at the earliest opportunity.
2454    #[tracing::instrument(
2455        name = "conncheck_incoming_data",
2456        level = "trace",
2457        ret(Display),
2458        skip(self, transmit)
2459        fields(
2460            transport = %transmit.transport,
2461            from = %transmit.from,
2462            to = %transmit.to,
2463            len = transmit.data.as_ref().len(),
2464        )
2465    )]
2466    pub fn incoming_data<T: AsRef<[u8]> + core::fmt::Debug>(
2467        &mut self,
2468        checklist_id: usize,
2469        component_id: usize,
2470        mut transmit: Transmit<T>,
2471        now: Instant,
2472    ) -> HandleRecvReply<T> {
2473        let Some(mut checklist_i) = self
2474            .checklists
2475            .iter()
2476            .position(|cl| cl.checklist_id == checklist_id)
2477        else {
2478            warn!("no such checklist with id {checklist_id}");
2479            return HandleRecvReply::default();
2480        };
2481
2482        // first de-TURN any incoming data
2483        let mut turn_client_id = None;
2484        if let Some((turn_id, turn_server_addr, checklist_i2)) = self.checklists[checklist_i]
2485            .find_turn_client_for_5tuple(transmit.transport, transmit.to, transmit.from)
2486            .map(|(id, client)| (id, client.remote_addr(), checklist_i))
2487            .or_else(|| {
2488                self.checklists.iter().find_map(|checklist| {
2489                    checklist
2490                        .find_turn_client_for_5tuple(transmit.transport, transmit.to, transmit.from)
2491                        .map(|(id, client)| (id, client.remote_addr(), checklist_i))
2492                })
2493            })
2494        {
2495            let client = self.checklists[checklist_i]
2496                .mut_turn_client_by_id(turn_id)
2497                .unwrap();
2498            match client.recv(transmit, now) {
2499                TurnRecvRet::Handled => {
2500                    // TODO: maybe handle turn events here?
2501                    trace!("TURN client handled the incoming data");
2502                    return HandleRecvReply {
2503                        handled: true,
2504                        have_more_data: false,
2505                        data: None,
2506                    };
2507                }
2508                TurnRecvRet::Ignored(ignored) => {
2509                    transmit = ignored;
2510                }
2511                TurnRecvRet::PeerData(peer) => {
2512                    let turn_client_transport = client.transport();
2513                    turn_client_id = Some((turn_id, turn_server_addr));
2514                    checklist_i = checklist_i2;
2515                    // FIXME: dual allocation TURN
2516                    let transmit = Transmit::new(
2517                        peer.data(),
2518                        peer.transport,
2519                        peer.peer,
2520                        client.relayed_addresses().next().unwrap().1,
2521                    );
2522                    let ret = self.incoming_data_or_stun(
2523                        checklist_i,
2524                        component_id,
2525                        transmit,
2526                        turn_client_id,
2527                    );
2528                    if let Some(data) = ret.data.as_ref() {
2529                        let checklist = &mut self.checklists[checklist_i];
2530                        checklist.pending_recv.push_back(PendingRecv {
2531                            component_id,
2532                            data: data.as_ref().to_vec(),
2533                        });
2534                    }
2535                    if turn_client_transport != TransportType::Udp {
2536                        loop {
2537                            let client = self.checklists[checklist_i]
2538                                .mut_turn_client_by_id(turn_id)
2539                                .unwrap();
2540                            let Some(peer) = client.poll_recv(now) else {
2541                                break;
2542                            };
2543                            let transmit = Transmit::new(
2544                                peer.data(),
2545                                peer.transport,
2546                                peer.peer,
2547                                client.relayed_addresses().next().unwrap().1,
2548                            );
2549                            let ret = self.incoming_data_or_stun(
2550                                checklist_i,
2551                                component_id,
2552                                transmit,
2553                                turn_client_id,
2554                            );
2555                            if let Some(data) = ret.data.as_ref() {
2556                                let checklist = &mut self.checklists[checklist_i];
2557                                checklist.pending_recv.push_back(PendingRecv {
2558                                    component_id,
2559                                    data: data.as_ref().to_vec(),
2560                                });
2561                            }
2562                        }
2563                    }
2564                    return HandleRecvReply {
2565                        handled: ret.handled,
2566                        have_more_data: true,
2567                        data: None,
2568                    };
2569                }
2570                TurnRecvRet::PeerIcmp {
2571                    transport,
2572                    peer,
2573                    icmp_type,
2574                    icmp_code,
2575                    icmp_data: _,
2576                } => {
2577                    debug!(
2578                        "conncheck received ICMP(type:{icmp_type:x}, code:{icmp_code:x}) over TURN from {transport}:{peer}"
2579                    );
2580                    return HandleRecvReply {
2581                        handled: true,
2582                        have_more_data: false,
2583                        data: None,
2584                    };
2585                }
2586            }
2587        }
2588
2589        self.incoming_data_or_stun(checklist_i, component_id, transmit, turn_client_id)
2590    }
2591
2592    #[allow(clippy::too_many_arguments)]
2593    fn handle_binding_request(
2594        &mut self,
2595        checklist_i: usize,
2596        local: &Candidate,
2597        agent_id: StunAgentId,
2598        msg: &Message,
2599        from: SocketAddr,
2600    ) -> MessageWriteVec {
2601        let checklist = &mut self.checklists[checklist_i];
2602        trace!("have request {}", msg);
2603
2604        if let Some(error_msg) = Message::check_attribute_types(
2605            msg,
2606            &[
2607                Username::TYPE,
2608                Fingerprint::TYPE,
2609                MessageIntegrity::TYPE,
2610                IceControlled::TYPE,
2611                IceControlling::TYPE,
2612                Priority::TYPE,
2613                UseCandidate::TYPE,
2614            ],
2615            &[
2616                Username::TYPE,
2617                Fingerprint::TYPE,
2618                MessageIntegrity::TYPE,
2619                Priority::TYPE,
2620            ],
2621            MessageWriteVec::new(),
2622        ) {
2623            // failure -> send error response
2624            return error_msg;
2625        }
2626        let peer_nominating = if let Some(use_candidate_raw) = msg.raw_attribute(UseCandidate::TYPE)
2627        {
2628            if UseCandidate::from_raw(use_candidate_raw).is_ok() {
2629                true
2630            } else {
2631                return Message::bad_request(msg, MessageWriteVec::new());
2632            }
2633        } else {
2634            false
2635        };
2636
2637        let priority = match msg.attribute::<Priority>() {
2638            Ok(p) => p.priority(),
2639            Err(_) => {
2640                return Message::bad_request(msg, MessageWriteVec::new());
2641            }
2642        };
2643
2644        let ice_controlling = msg.attribute::<IceControlling>();
2645        let ice_controlled = msg.attribute::<IceControlled>();
2646
2647        // validate username
2648        if let Ok(username) = msg.attribute::<Username>() {
2649            if !validate_username(username, &checklist.local_credentials) {
2650                warn!("binding request failed username validation -> UNAUTHORIZED");
2651                let mut response = Message::builder_error(msg, MessageWriteVec::new());
2652                let error = ErrorCode::builder(ErrorCode::UNAUTHORIZED).build().unwrap();
2653                response.add_attribute(&error).unwrap();
2654                return response;
2655            }
2656        } else {
2657            // existence is checked above so can only fail when the username is invalid
2658            return Message::bad_request(msg, MessageWriteVec::new());
2659        }
2660
2661        // Deal with role conflicts
2662        // RFC 8445 7.3.1.1.  Detecting and Repairing Role Conflicts
2663        trace!("checking for role conflicts");
2664        if let Ok(ice_controlling) = ice_controlling {
2665            //  o  If the agent is in the controlling role, and the ICE-CONTROLLING
2666            //     attribute is present in the request:
2667            if self.controlling {
2668                if self.tie_breaker >= ice_controlling.tie_breaker() {
2669                    debug!("role conflict detected (controlling=true), returning ROLE_CONFLICT");
2670                    // *  If the agent's tiebreaker value is larger than or equal to the
2671                    //    contents of the ICE-CONTROLLING attribute, the agent generates
2672                    //    a Binding error response and includes an ERROR-CODE attribute
2673                    //    with a value of 487 (Role Conflict) but retains its role.
2674                    let mut response = Message::builder_error(msg, MessageWriteVec::new());
2675                    let error = ErrorCode::builder(ErrorCode::ROLE_CONFLICT)
2676                        .build()
2677                        .unwrap();
2678                    response.add_attribute(&error).unwrap();
2679                    return response_add_credentials(
2680                        response,
2681                        &mut self.checklists[checklist_i].stun_auth_local,
2682                    )
2683                    .unwrap();
2684                } else {
2685                    debug!("role conflict detected, updating controlling state to false");
2686                    // *  If the agent's tiebreaker value is less than the contents of
2687                    //    the ICE-CONTROLLING attribute, the agent switches to the
2688                    //    controlled role.
2689                    self.controlling = false;
2690                    for l in self.checklists.iter_mut() {
2691                        l.set_controlling(false);
2692                    }
2693                }
2694            }
2695        }
2696        if let Ok(ice_controlled) = ice_controlled {
2697            // o  If the agent is in the controlled role, and the ICE-CONTROLLED
2698            //    attribute is present in the request:
2699            if !self.controlling {
2700                if self.tie_breaker >= ice_controlled.tie_breaker() {
2701                    debug!("role conflict detected, updating controlling state to true");
2702                    // *  If the agent's tiebreaker value is larger than or equal to the
2703                    //    contents of the ICE-CONTROLLED attribute, the agent switches to
2704                    //    the controlling role.
2705                    self.controlling = true;
2706                    for l in self.checklists.iter_mut() {
2707                        l.set_controlling(true);
2708                    }
2709                } else {
2710                    debug!("role conflict detected (controlling=false), returning ROLE_CONFLICT");
2711                    // *  If the agent's tiebreaker value is less than the contents of
2712                    //    the ICE-CONTROLLED attribute, the agent generates a Binding
2713                    //    error response and includes an ERROR-CODE attribute with a
2714                    //    value of 487 (Role Conflict) but retains its role.
2715                    let mut response = Message::builder_error(msg, MessageWriteVec::new());
2716                    let error = ErrorCode::builder(ErrorCode::ROLE_CONFLICT)
2717                        .build()
2718                        .unwrap();
2719                    response.add_attribute(&error).unwrap();
2720                    return response_add_credentials(
2721                        response,
2722                        &mut self.checklists[checklist_i].stun_auth_local,
2723                    )
2724                    .unwrap();
2725                }
2726            }
2727        }
2728        trace!("checked for role conflicts");
2729        let checklist = &mut self.checklists[checklist_i];
2730        let remote = checklist
2731            .find_remote_candidate(local.component_id, local.transport_type, from)
2732            .unwrap_or_else(|| {
2733                debug!("no existing remote candidate for {from}");
2734                // If the source transport address of the request does not match any
2735                // existing remote candidates, it represents a new peer-reflexive remote
2736                // candidate.  This candidate is constructed as follows:
2737                //
2738                //   o  The priority is the value of the PRIORITY attribute in the Binding
2739                //      request.
2740                //   o  The type is peer reflexive.
2741                //   o  The component ID is the component ID of the local candidate to
2742                //      which the request was sent.
2743                //   o  The foundation is an arbitrary value, different from the
2744                //      foundations of all other remote candidates.  If any subsequent
2745                //      candidate exchanges contain this peer-reflexive candidate, it will
2746                //      signal the actual foundation for the candidate
2747                let mut builder = Candidate::builder(
2748                    local.component_id,
2749                    CandidateType::PeerReflexive,
2750                    local.transport_type,
2751                    /* FIXME */ "rflx",
2752                    from,
2753                )
2754                .priority(priority);
2755                if local.transport_type == TransportType::Tcp {
2756                    builder = builder.tcp_type(pair_tcp_type(local.tcp_type.unwrap()))
2757                }
2758                let cand = builder.build();
2759                debug!("new reflexive remote {:?}", cand);
2760                checklist.add_remote_candidate(cand.clone());
2761                cand
2762            });
2763        trace!("remote candidate {remote:?}");
2764        // RFC 8445 Section 7.3.1.4. Triggered Checks
2765        let pair = CandidatePair::new(local.clone(), remote);
2766        if let Some(mut check) = checklist.take_matching_check(&pair, Nominate::DontCare) {
2767            // When the pair is already on the checklist:
2768            trace!("found existing {:?} check {:?}", check.state(), check);
2769            match check.state() {
2770                // If the state of that pair is Succeeded, nothing further is
2771                // done.
2772                CandidatePairState::Succeeded => {
2773                    if peer_nominating && !check.nominate() {
2774                        debug!("existing pair succeeded -> nominate");
2775                        let pair = check.pair.clone();
2776                        let agent_id = check.agent_id().unwrap();
2777                        let mut new_check = ConnCheck::new(
2778                            checklist.checklist_id,
2779                            pair.clone(),
2780                            agent_id,
2781                            true,
2782                            self.controlling,
2783                            checklist.remote_credentials.clone(),
2784                        );
2785                        checklist.add_check(check);
2786                        new_check.set_state(CandidatePairState::Waiting);
2787                        checklist.add_valid(new_check.conncheck_id, &pair);
2788                        checklist.add_triggered(&new_check);
2789                        checklist.add_check(new_check);
2790                    } else {
2791                        checklist.add_check(check);
2792                    }
2793                }
2794                // If the state of that pair is In-Progress, the agent cancels the
2795                // In-Progress transaction.  Cancellation means that the agent
2796                // will not retransmit the Binding requests associated with the
2797                // connectivity-check transaction, will not treat the lack of
2798                // response to be a failure, but will wait the duration of the
2799                // transaction timeout for a response.  In addition, the agent
2800                // MUST enqueue the pair in the triggered checklist associated
2801                // with the checklist, and set the state of the pair to Waiting,
2802                // in order to trigger a new connectivity check of the pair.
2803                // Creating a new connectivity check enables validating
2804                // In-Progress pairs as soon as possible, without having to wait
2805                // for retransmissions of the Binding requests associated with the
2806                // original connectivity-check transaction.
2807                CandidatePairState::InProgress => {
2808                    let old_check_id = check.conncheck_id;
2809                    let pair = check.pair.clone();
2810                    // TODO: ignore response timeouts
2811
2812                    let agent_id = check.agent_id().unwrap();
2813                    let mut new_check = ConnCheck::new(
2814                        checklist.checklist_id,
2815                        pair,
2816                        agent_id,
2817                        peer_nominating,
2818                        self.controlling,
2819                        checklist.remote_credentials.clone(),
2820                    );
2821                    checklist.check_cancel_retransmissions(old_check_id);
2822                    checklist.add_check(check);
2823                    new_check.set_state(CandidatePairState::Waiting);
2824                    checklist.add_triggered(&new_check);
2825                    checklist.add_check(new_check);
2826                }
2827                // If the state of that pair is Waiting, Frozen, or Failed, the
2828                // agent MUST enqueue the pair in the triggered checklist
2829                // associated with the checklist (if not already present), and set
2830                // the state of the pair to Waiting, in order to trigger a new
2831                // connectivity check of the pair.  Note that a state change of
2832                // the pair from Failed to Waiting might also trigger a state
2833                // change of the associated checklist.
2834                CandidatePairState::Waiting
2835                | CandidatePairState::Frozen
2836                | CandidatePairState::Failed => {
2837                    let mut old_check_id = None;
2838                    if peer_nominating && !check.nominate() {
2839                        old_check_id = Some(check.conncheck_id);
2840                        check = ConnCheck::new(
2841                            checklist.checklist_id,
2842                            check.pair.clone(),
2843                            agent_id,
2844                            peer_nominating,
2845                            self.controlling,
2846                            checklist.remote_credentials.clone(),
2847                        );
2848                    }
2849                    check.set_state(CandidatePairState::Waiting);
2850                    if let Some(old_check_id) = old_check_id {
2851                        checklist.check_cancel(old_check_id);
2852                    }
2853                    checklist.add_triggered(&check);
2854                    checklist.add_check(check);
2855                }
2856            }
2857        } else {
2858            debug!("creating new check for pair {:?}", pair);
2859            let mut check = ConnCheck::new(
2860                checklist.checklist_id,
2861                pair,
2862                agent_id,
2863                peer_nominating,
2864                self.controlling,
2865                checklist.remote_credentials.clone(),
2866            );
2867            check.set_state(CandidatePairState::Waiting);
2868            checklist.add_triggered(&check);
2869            checklist.add_check(check);
2870        }
2871
2872        if let Some(agent) = self.checklists[checklist_i].mut_agent_by_id(agent_id) {
2873            agent.validated_peer(from);
2874        }
2875
2876        binding_success_response(msg, from, &mut self.checklists[checklist_i].stun_auth_local)
2877    }
2878
2879    fn check_success(
2880        &mut self,
2881        checklist_i: usize,
2882        conncheck_id: ConnCheckId,
2883        addr: SocketAddr,
2884        controlling: bool,
2885    ) {
2886        let checklist = &mut self.checklists[checklist_i];
2887        let checklist_id = checklist.checklist_id;
2888        checklist.check_cancel_retransmissions(conncheck_id);
2889        let conncheck = checklist.mut_check_by_id(conncheck_id).unwrap();
2890        let conncheck_id = conncheck.conncheck_id;
2891        let nominate = conncheck.nominate();
2892        info!(
2893            component.id = conncheck.pair.local.component_id,
2894            nominate = conncheck.nominate,
2895            ttype = ?conncheck.pair.local.transport_type,
2896            local.base_address = ?conncheck.pair.local.base_address,
2897            remote.address = ?conncheck.pair.remote.address,
2898            local.ctype = ?conncheck.pair.local.candidate_type,
2899            remote.ctype = ?conncheck.pair.remote.candidate_type,
2900            foundation = %conncheck.pair.foundation(),
2901            xor_mapped_address = ?addr,
2902            "succeeded in finding a connection"
2903        );
2904        conncheck.set_state(CandidatePairState::Succeeded);
2905        let pair = conncheck.pair.clone();
2906        let ok_pair = pair_construct_valid(&pair, addr);
2907        let agent_id = conncheck.agent_id().unwrap();
2908        let mut ok_check = ConnCheck::new(
2909            checklist_id,
2910            ok_pair.clone(),
2911            agent_id,
2912            false,
2913            self.controlling,
2914            checklist.remote_credentials.clone(),
2915        );
2916
2917        if checklist.state != CheckListState::Running {
2918            debug!("checklist is not running, ignoring check response");
2919            return;
2920        }
2921
2922        let mut pair_dealt_with = false;
2923        // 1.
2924        // If the valid pair equals the pair that generated the check, the
2925        // pair is added to the valid list associated with the checklist to
2926        // which the pair belongs; or
2927        if candidate_pair_is_same_connection(&pair, &ok_pair) {
2928            debug!(existing.id = ?conncheck_id, "found existing check");
2929            let checklist = &mut self.checklists[checklist_i];
2930            checklist.add_valid(conncheck_id, &pair);
2931            if nominate {
2932                checklist.nominated_pair(&pair);
2933                return;
2934            }
2935            pair_dealt_with = true;
2936        } else {
2937            // 2.
2938            // If the valid pair equals another pair in a checklist, that pair
2939            // is added to the valid list associated with the checklist of that
2940            // pair.  The pair that generated the check is not added to a vali
2941            // list; or
2942            for checklist in self.checklists.iter_mut() {
2943                if let Some(check) = checklist.matching_check(&ok_pair, Nominate::DontCare) {
2944                    debug!(
2945                        existing.id = *check.conncheck_id,
2946                        "found existing check in checklist {}", checklist.checklist_id
2947                    );
2948                    checklist.add_valid(check.conncheck_id, &pair);
2949                    if nominate {
2950                        checklist.nominated_pair(&pair);
2951                        return;
2952                    }
2953                    pair_dealt_with = true;
2954                    break;
2955                }
2956            }
2957        }
2958        let checklist = &mut self.checklists[checklist_i];
2959        // 3.
2960        // If the valid pair is not in any checklist, the agent computes the
2961        // priority for the pair based on the priority of each candidate,
2962        // using the algorithm in Section 6.1.2.  The priority of the local
2963        // candidate depends on its type.  Unless the type is peer
2964        // reflexive, the priority is equal to the priority signaled for
2965        // that candidate in the candidate exchange.  If the type is peer
2966        // reflexive, it is equal to the PRIORITY attribute the agent placed
2967        // in the Binding request that just completed.  The priority of the
2968        // remote candidate is taken from the candidate information of the
2969        // peer.  If the candidate does not appear there, then the check has
2970        // been a triggered check to a new remote candidate.  In that case,
2971        // the priority is taken as the value of the PRIORITY attribute in
2972        // the Binding request that triggered the check that just completed.
2973        // The pair is then added to the valid list.
2974        if !pair_dealt_with {
2975            debug!("no existing check");
2976            // TODO: need to construct correct pair priorities and foundations,
2977            // just use whatever the conncheck produced for now
2978            ok_check.set_state(CandidatePairState::Succeeded);
2979            let ok_id = ok_check.conncheck_id;
2980            if checklist.add_check_if_not_duplicate(ok_check) {
2981                checklist.add_valid(ok_id, &ok_pair);
2982            }
2983            checklist.add_valid(conncheck_id, &pair);
2984
2985            if nominate {
2986                checklist.nominated_pair(&pair);
2987                return;
2988            }
2989        }
2990        // Try and nominate some pair
2991        if controlling {
2992            checklist.try_nominate();
2993        }
2994    }
2995
2996    #[tracing::instrument(
2997        skip(self, response),
2998        fields(
2999            checklist_id = self.checklists[checklist_i].checklist_id,
3000        ),
3001    )]
3002    fn handle_stun_response(&mut self, checklist_i: usize, response: &Message, from: SocketAddr) {
3003        let checklist = &mut self.checklists[checklist_i];
3004        let checklist_id = checklist.checklist_id;
3005        // find conncheck
3006        let conncheck = checklist.mut_check_from_stun_response(response.transaction_id(), from);
3007        let conncheck = match conncheck {
3008            Some(conncheck) => conncheck,
3009            None => {
3010                checklist.dump_check_state();
3011                warn!("No existing check available, ignoring");
3012                return;
3013            }
3014        };
3015        let conncheck_id = conncheck.conncheck_id;
3016
3017        // if response success:
3018        // if mismatched address -> fail
3019        if from != conncheck.pair.remote.address {
3020            warn!(
3021                "response came from different ip {:?} than candidate {:?}",
3022                from, conncheck.pair.remote.address
3023            );
3024            checklist.check_response_failure(conncheck_id);
3025            return;
3026        }
3027
3028        // if response error -> fail TODO: might be a recoverable error!
3029        if response.has_class(MessageClass::Error) {
3030            if let Ok(err) = response.attribute::<ErrorCode>() {
3031                if err.code() == ErrorCode::ROLE_CONFLICT {
3032                    info!("Role conflict received {}", response);
3033                    let new_role = !conncheck.controlling;
3034                    info!(
3035                        old_role = self.controlling,
3036                        new_role, "Role Conflict changing controlling from"
3037                    );
3038                    if self.controlling != new_role {
3039                        let old_pair = conncheck.pair.clone();
3040                        let old_conncheck_id = conncheck.conncheck_id;
3041                        self.controlling = new_role;
3042                        let agent_id = conncheck.agent_id().unwrap();
3043                        let mut conncheck = ConnCheck::new(
3044                            checklist_id,
3045                            conncheck.pair.clone(),
3046                            agent_id,
3047                            false,
3048                            self.controlling,
3049                            checklist.remote_credentials.clone(),
3050                        );
3051                        conncheck.set_state(CandidatePairState::Waiting);
3052                        checklist.check_cancel(old_conncheck_id);
3053                        checklist.add_triggered(&conncheck);
3054                        checklist.add_check(conncheck);
3055                        self.checklists[checklist_i].remove_valid(&old_pair);
3056                    }
3057                    return;
3058                }
3059            }
3060            // FIXME: some failures are recoverable
3061            warn!("error response {}", response);
3062            self.checklists[checklist_i].check_response_failure(conncheck_id);
3063        }
3064
3065        if let Ok(xor) = response.attribute::<XorMappedAddress>() {
3066            let xor_addr = xor.addr(response.transaction_id());
3067            return self.check_success(checklist_i, conncheck_id, xor_addr, self.controlling);
3068        }
3069
3070        self.checklists[checklist_i].check_response_failure(conncheck_id);
3071    }
3072
3073    fn perform_conncheck(
3074        &mut self,
3075        checklist_i: usize,
3076        conncheck_id: ConnCheckId,
3077        now: Instant,
3078    ) -> Result<Option<CheckListSetSocket>, StunError> {
3079        let checklist_id = self.checklists[self.checklist_i].checklist_id;
3080        let checklist = &mut self.checklists[checklist_i];
3081        let local_credentials = checklist.local_credentials.clone();
3082        let remote_credentials = checklist.remote_credentials.clone();
3083
3084        let conncheck = checklist.mut_check_by_id(conncheck_id).unwrap();
3085        let turn_id = {
3086            let transport = conncheck.pair.local.transport_type;
3087            let base_addr = conncheck.pair.local.base_address;
3088            checklist
3089                .turn_client_by_allocated_address(transport, base_addr)
3090                .map(|(id, client)| (id, client.remote_addr()))
3091        };
3092        let conncheck = checklist.mut_check_by_id(conncheck_id).unwrap();
3093        let component_id = conncheck.pair.local.component_id;
3094        for (cid, state) in checklist.component_ids.iter_mut() {
3095            if *cid == component_id
3096                && [
3097                    ComponentConnectionState::New,
3098                    ComponentConnectionState::Failed,
3099                ]
3100                .contains(state)
3101            {
3102                *state = ComponentConnectionState::Connecting;
3103                checklist.events.push_front(ConnCheckEvent::ComponentState(
3104                    component_id,
3105                    ComponentConnectionState::Connecting,
3106                ));
3107            }
3108        }
3109        let conncheck = checklist.mut_check_by_id(conncheck_id).unwrap();
3110
3111        debug!("starting connectivity check {}", conncheck.conncheck_id);
3112        if conncheck.stun_request.is_some() {
3113            panic!("Attempt was made to start an already started check");
3114        }
3115
3116        let agent_id = match &conncheck.variant {
3117            ConnCheckVariant::Tcp(_tcp) => {
3118                if let Some((turn_id, turn_addr)) = turn_id {
3119                    let relayed_addr = conncheck.pair.local.base_address;
3120                    let remote_addr = conncheck.pair.remote.address;
3121                    let conncheck_id = conncheck.conncheck_id;
3122                    checklist.pending_turn_tcp_connect.push(PendingTurnTcp {
3123                        turn_id,
3124                        conncheck_id,
3125                        relayed_addr,
3126                        turn_addr,
3127                        peer_addr: remote_addr,
3128                        turn_connect_id: None,
3129                        allocated: false,
3130                    });
3131                    let Some(client) = checklist.mut_turn_client_by_id(turn_id) else {
3132                        checklist.pending_turn_tcp_connect.pop();
3133                        return Ok(None);
3134                    };
3135                    client.tcp_connect(remote_addr, now).unwrap();
3136                    return Ok(None);
3137                } else {
3138                    return Ok(Some(CheckListSetSocket {
3139                        checklist_id,
3140                        component_id: conncheck.pair.local.component_id,
3141                        transport: TransportType::Tcp,
3142                        local_addr: conncheck.pair.local.base_address,
3143                        remote_addr: conncheck.pair.remote.address,
3144                    }));
3145                }
3146            }
3147            ConnCheckVariant::Agent(agent_id) => agent_id,
3148        };
3149
3150        let stun_request = ConnCheck::generate_stun_request(
3151            &conncheck.pair,
3152            conncheck.nominate,
3153            self.controlling,
3154            self.tie_breaker,
3155            local_credentials,
3156            remote_credentials,
3157        )
3158        .unwrap();
3159        conncheck.stun_request = Some(stun_request.transaction_id());
3160        conncheck.controlling = self.controlling;
3161        let remote_addr = conncheck.pair.remote.address;
3162
3163        self.pending_messages
3164            .push_front(CheckListSetPendingMessage {
3165                checklist_id,
3166                agent_id: *agent_id,
3167                turn_id,
3168                is_request: true,
3169                msg: stun_request.finish(),
3170                to: remote_addr,
3171            });
3172        Ok(None)
3173    }
3174
3175    #[tracing::instrument(
3176        level = "info",
3177        ret,
3178        skip(self),
3179        fields(
3180             checklist_id = self.checklists[self.checklist_i].checklist_id,
3181        ),
3182    )]
3183    // RFC8445: 6.1.4.2. Performing Connectivity Checks
3184    fn next_check(&mut self) -> Option<ConnCheckId> {
3185        let checklist = &mut self.checklists[self.checklist_i];
3186        {
3187            if checklist.state != CheckListState::Running {
3188                // A non-running checklist has no next check
3189                return None;
3190            }
3191            checklist.dump_check_state();
3192        }
3193
3194        // 1.  If the triggered-check queue associated with the checklist
3195        //     contains one or more candidate pairs, the agent removes the top
3196        //     pair from the queue, performs a connectivity check on that pair,
3197        //     puts the candidate pair state to In-Progress, and aborts the
3198        //     subsequent steps.
3199        if let Some(check) = checklist.next_triggered() {
3200            trace!("next check was a triggered check {:?}", check);
3201            Some(check.conncheck_id)
3202        // 3.  If there are one or more candidate pairs in the Waiting state,
3203        //     the agent picks the highest-priority candidate pair (if there are
3204        //     multiple pairs with the same priority, the pair with the lowest
3205        //     component ID is picked) in the Waiting state, performs a
3206        //     connectivity check on that pair, puts the candidate pair state to
3207        //     In-Progress, and aborts the subsequent steps.
3208        } else if let Some(check) = checklist.next_waiting() {
3209            trace!("next check was a waiting check {:?}", check);
3210            Some(check.conncheck_id)
3211        } else {
3212            // TODO: cache this locally somewhere
3213            // TODO: iter()ize this
3214            // 2.  If there is no candidate pair in the Waiting state, and if there
3215            //     are one or more pairs in the Frozen state, the agent checks the
3216            //     foundation associated with each pair in the Frozen state.  For a
3217            //     given foundation, if there is no pair (in any checklist in the
3218            //     checklist set) in the Waiting or In-Progress state, the agent
3219            //     puts the candidate pair state to Waiting and continues with the
3220            //     next step.
3221            let mut foundations_not_waiting_in_progress = BTreeSet::new();
3222            for checklist in self.checklists.iter() {
3223                for f in checklist.foundations() {
3224                    if self
3225                        .checklists
3226                        .iter()
3227                        .all(|checklist| checklist.foundation_not_waiting_in_progress(&f))
3228                    {
3229                        foundations_not_waiting_in_progress.insert(f);
3230                    }
3231                }
3232            }
3233            trace!(
3234                "current foundations not waiting or in progress: {:?}",
3235                foundations_not_waiting_in_progress
3236            );
3237
3238            let mut foundations_check_added = BTreeSet::new();
3239            for checklist in self.checklists.iter_mut() {
3240                for check in checklist.pairs.iter_mut() {
3241                    if check.state() != CandidatePairState::Frozen {
3242                        continue;
3243                    }
3244                    if !foundations_not_waiting_in_progress
3245                        .iter()
3246                        .any(|f| f == &check.pair.foundation())
3247                    {
3248                        continue;
3249                    }
3250                    if foundations_check_added
3251                        .iter()
3252                        .any(|f| f == &check.pair.foundation())
3253                    {
3254                        continue;
3255                    }
3256                    check.set_state(CandidatePairState::Waiting);
3257                    foundations_check_added.insert(check.pair.foundation());
3258                }
3259            }
3260
3261            let checklist = &mut self.checklists[self.checklist_i];
3262            if let Some(check) = checklist.next_waiting() {
3263                trace!("next check was a frozen check {:?}", check);
3264                check.set_state(CandidatePairState::InProgress);
3265                Some(check.conncheck_id)
3266            } else {
3267                // XXX: may need to return a check from a different checklist
3268                trace!("no next check for stream");
3269                None
3270            }
3271        }
3272    }
3273
3274    fn remove_check_resources(&mut self, checklist_i: usize, check: ConnCheck, now: Instant) {
3275        let ConnCheckVariant::Agent(check_agent_id) = check.variant else {
3276            return;
3277        };
3278        let checklist = &mut self.checklists[checklist_i];
3279        let checklist_id = checklist.checklist_id;
3280        if checklist.pairs.iter().any(|pair| {
3281            if let ConnCheckVariant::Agent(agent_id) = pair.variant {
3282                agent_id == check_agent_id
3283            } else {
3284                false
3285            }
3286        }) {
3287            return;
3288        }
3289
3290        let Some(agent) = checklist.agent_by_id(check_agent_id) else {
3291            return;
3292        };
3293
3294        let transport = agent.transport();
3295        let local_addr = agent.local_addr();
3296        let remote_addr = agent.remote_addr();
3297        debug!("found agent {transport}: {local_addr} -> {remote_addr:?} to maybe remove");
3298
3299        let base_addr = check.pair.local.base_address;
3300
3301        if let Some((turn_id, _turn_client)) =
3302            checklist.mut_turn_client_by_allocated_address(transport, base_addr)
3303        {
3304            if self.checklists.iter().any(|checklist| {
3305                checklist.pairs.iter().any(|pair| {
3306                    pair.pair.local.candidate_type == CandidateType::Relayed
3307                        && pair.pair.local.base_address == base_addr
3308                })
3309            }) {
3310                // TODO: remove permission for remote address here
3311                warn!(
3312                    "should remove {transport} permission from {local_addr} to {}",
3313                    check.pair.remote.address.ip()
3314                );
3315                return;
3316            }
3317            let checklist = &mut self.checklists[checklist_i];
3318            let mut turn_client = checklist.remove_turn_client_by_id(turn_id).unwrap();
3319            let _ = turn_client.delete(now);
3320            let checklist = &mut self.checklists[checklist_i];
3321            checklist.pending_delete_turn_clients.push(CheckTurnClient {
3322                id: turn_id,
3323                client: turn_client,
3324                local_tcp_sockets: Default::default(),
3325            });
3326            // socket remove will occur when the delete reply is received, or on timeout
3327            return;
3328        }
3329        let checklist = &mut self.checklists[checklist_i];
3330        if checklist
3331            .turn_clients
3332            .iter()
3333            .chain(checklist.pending_delete_turn_clients.iter())
3334            .any(|client| {
3335                client.client.transport() == transport
3336                    && client.client.local_addr() == local_addr
3337                    && if transport == TransportType::Tcp {
3338                        remote_addr == Some(client.client.remote_addr())
3339                    } else {
3340                        true
3341                    }
3342            })
3343        {
3344            return;
3345        }
3346        match transport {
3347            TransportType::Udp => {
3348                self.pending_remove_sockets.push_back(CheckListSetSocket {
3349                    checklist_id,
3350                    component_id: check.pair.local.component_id,
3351                    transport,
3352                    local_addr,
3353                    remote_addr: "0.0.0.0:0".parse().unwrap(),
3354                });
3355            }
3356            TransportType::Tcp => {
3357                self.pending_remove_sockets.push_back(CheckListSetSocket {
3358                    checklist_id,
3359                    component_id: check.pair.local.component_id,
3360                    transport,
3361                    local_addr,
3362                    remote_addr: remote_addr.unwrap(),
3363                });
3364            }
3365        }
3366    }
3367
3368    fn maybe_remove_sockets(&mut self, checklist_i: usize, now: Instant) {
3369        let checklist = &mut self.checklists[checklist_i];
3370        for failed in checklist.prune_checks_with(|check| check.state == CandidatePairState::Failed)
3371        {
3372            self.remove_check_resources(checklist_i, failed, now);
3373        }
3374    }
3375
3376    /// Advance the set state machine.  Should be called repeatedly until
3377    /// [`CheckListSetPollRet::WaitUntil`] is returned.
3378    #[tracing::instrument(name = "check_set_poll", level = "debug", ret, skip(self))]
3379    pub fn poll(&mut self, now: Instant) -> CheckListSetPollRet {
3380        if !self.pending_transmits.is_empty() || !self.pending_messages.is_empty() {
3381            trace!("have pending transmits/messages");
3382            return CheckListSetPollRet::WaitUntil(now);
3383        }
3384
3385        let mut n_running = 0;
3386        let mut all_failed = true;
3387        let mut all_turn_closed = self.closed;
3388        let start_idx = self.checklist_i;
3389        loop {
3390            let mut lowest_wait = now + Duration::from_secs(99999);
3391            if self.checklists.is_empty() {
3392                if self.closed {
3393                    return CheckListSetPollRet::Closed;
3394                }
3395                return CheckListSetPollRet::WaitUntil(lowest_wait);
3396            }
3397            self.checklist_i += 1;
3398            if self.checklist_i >= self.checklists.len() {
3399                self.checklist_i = 0;
3400            }
3401
3402            let checklist = &mut self.checklists[self.checklist_i];
3403            while let Some((turn_id, transport, remote_ip)) =
3404                checklist.pending_turn_permissions.pop_back()
3405            {
3406                trace!(
3407                    "have pending turn permission for id {turn_id:?}, {transport:?}, {remote_ip}"
3408                );
3409                let Some(client) = checklist.mut_turn_client_by_id(turn_id) else {
3410                    continue;
3411                };
3412
3413                if let Err(e) = client.create_permission(transport, remote_ip, now) {
3414                    warn!(
3415                        "received error trying to create a permission to {:?}: {e}",
3416                        remote_ip
3417                    );
3418                }
3419            }
3420
3421            let checklist_id = checklist.checklist_id;
3422            for idx in 0..checklist.turn_clients.len() {
3423                let client = &mut checklist.turn_clients[idx];
3424                let turn_id = client.id;
3425                while let Some(event) = client.client.poll_event() {
3426                    match event {
3427                        TurnEvent::AllocationCreated(_, _) => (),
3428                        TurnEvent::AllocationCreateFailed(_family) => (),
3429                        TurnEvent::PermissionCreated(transport, peer_addr) => {
3430                            for idx in 0..checklist.pairs.len() {
3431                                let check = &mut checklist.pairs[idx];
3432                                if check.pair.local.candidate_type != CandidateType::Relayed
3433                                    || check.pair.remote.address.ip() != peer_addr
3434                                    || check.pair.local.transport_type != transport
3435                                    || !matches!(
3436                                        check.state,
3437                                        CandidatePairState::Failed | CandidatePairState::Succeeded
3438                                    )
3439                                {
3440                                    continue;
3441                                }
3442                            }
3443                        }
3444                        TurnEvent::PermissionCreateFailed(transport, peer_addr) => {
3445                            for idx in 0..checklist.pairs.len() {
3446                                let check = &mut checklist.pairs[idx];
3447                                if check.pair.local.candidate_type != CandidateType::Relayed
3448                                    || check.pair.remote.address.ip() != peer_addr
3449                                    || check.pair.local.transport_type != transport
3450                                {
3451                                    continue;
3452                                }
3453                                check.set_state(CandidatePairState::Failed);
3454                            }
3455                        }
3456                        TurnEvent::ChannelCreated(_transport, _peer_addr) => (),
3457                        TurnEvent::ChannelCreateFailed(_transport, _addr) => (),
3458                        TurnEvent::TcpConnected(peer_addr) => {
3459                            let Some(idx) =
3460                                checklist
3461                                    .pending_turn_tcp_connect
3462                                    .iter()
3463                                    .position(|pending| {
3464                                        pending.peer_addr == peer_addr && pending.turn_id == turn_id
3465                                    })
3466                            else {
3467                                continue;
3468                            };
3469                            let pending = checklist.pending_turn_tcp_connect.swap_remove(idx);
3470                            let local_credentials = checklist.local_credentials.clone();
3471                            let remote_credentials = checklist.remote_credentials.clone();
3472                            let Some(conncheck) = checklist
3473                                .pairs
3474                                .iter_mut()
3475                                .find(|check| check.conncheck_id == pending.conncheck_id)
3476                            else {
3477                                continue;
3478                            };
3479                            let agent_id = if let Some(local_agent) =
3480                                checklist.agents.iter_mut().find(|a| {
3481                                    a.agent.transport() == TransportType::Tcp
3482                                        && a.agent.local_addr() == pending.relayed_addr
3483                                        && a.agent.remote_addr().unwrap() == pending.peer_addr
3484                                }) {
3485                                local_agent.id
3486                            } else {
3487                                let agent =
3488                                    StunAgent::builder(TransportType::Tcp, pending.relayed_addr)
3489                                        .remote_addr(pending.peer_addr)
3490                                        .build();
3491                                let agent_id = StunAgentId::generate();
3492                                checklist.agents.push(CheckStunAgent {
3493                                    id: agent_id,
3494                                    agent,
3495                                    turn_id: Some(turn_id),
3496                                });
3497                                agent_id
3498                            };
3499                            conncheck.variant = ConnCheckVariant::Agent(agent_id);
3500
3501                            let stun_request = ConnCheck::generate_stun_request(
3502                                &conncheck.pair,
3503                                conncheck.nominate,
3504                                self.controlling,
3505                                self.tie_breaker,
3506                                local_credentials,
3507                                remote_credentials,
3508                            )
3509                            .unwrap();
3510                            conncheck.stun_request = Some(stun_request.transaction_id());
3511                            conncheck.controlling = self.controlling;
3512                            self.pending_messages
3513                                .push_front(CheckListSetPendingMessage {
3514                                    checklist_id,
3515                                    agent_id,
3516                                    is_request: true,
3517                                    msg: stun_request.finish(),
3518                                    turn_id: Some((pending.turn_id, pending.turn_addr)),
3519                                    to: pending.peer_addr,
3520                                });
3521                        }
3522                        TurnEvent::TcpConnectFailed(peer_addr) => {
3523                            let Some(idx) = checklist
3524                                .pending_turn_tcp_connect
3525                                .iter()
3526                                .position(|pending| pending.peer_addr == peer_addr)
3527                            else {
3528                                continue;
3529                            };
3530                            let _pending = checklist.pending_turn_tcp_connect.swap_remove(idx);
3531                            checklist
3532                                .pairs
3533                                .retain(|check| check.pair.remote.address != peer_addr);
3534                        }
3535                    }
3536                }
3537
3538                match client.client.poll(now) {
3539                    TurnPollRet::Closed => (),
3540                    TurnPollRet::WaitUntil(wait) => {
3541                        all_turn_closed = false;
3542                        if wait == now {
3543                            return CheckListSetPollRet::WaitUntil(
3544                                wait.max(
3545                                    self.last_send_time
3546                                        .map(|last_send| last_send + self.timing_advance)
3547                                        .unwrap_or(now),
3548                                ),
3549                            );
3550                        }
3551                        if wait < lowest_wait {
3552                            lowest_wait = wait.max(
3553                                self.last_send_time
3554                                    .map(|last_send| last_send + self.timing_advance)
3555                                    .unwrap_or(now),
3556                            );
3557                        }
3558                    }
3559                    TurnPollRet::TcpClose {
3560                        local_addr,
3561                        remote_addr,
3562                    } => {
3563                        return CheckListSetPollRet::RemoveSocket {
3564                            checklist_id,
3565                            // FIXME: hardcoded component
3566                            component_id: 1,
3567                            transport: client.client.transport(),
3568                            local_addr,
3569                            remote_addr,
3570                        };
3571                    }
3572                    TurnPollRet::AllocateTcpSocket {
3573                        id,
3574                        socket,
3575                        peer_addr,
3576                    } => {
3577                        let Some(pending) =
3578                            checklist
3579                                .pending_turn_tcp_connect
3580                                .iter_mut()
3581                                .find(|pending| {
3582                                    pending.turn_id == turn_id
3583                                        && pending.peer_addr == peer_addr
3584                                        && pending.turn_addr == socket.to
3585                                })
3586                        else {
3587                            continue;
3588                        };
3589                        pending.turn_connect_id = Some(id);
3590                        return CheckListSetPollRet::AllocateSocket {
3591                            checklist_id,
3592                            component_id: 1,
3593                            transport: socket.transport,
3594                            local_addr: socket.from,
3595                            remote_addr: socket.to,
3596                        };
3597                    }
3598                }
3599            }
3600            let mut idx = 0;
3601            while let Some(client) = checklist.pending_delete_turn_clients.get_mut(idx) {
3602                match client.client.poll(now) {
3603                    TurnPollRet::Closed => {
3604                        let client = checklist.pending_delete_turn_clients.remove(idx).client;
3605                        let transport = client.transport();
3606                        if checklist
3607                            .find_agent_for_5tuple(
3608                                transport,
3609                                client.local_addr(),
3610                                client.remote_addr(),
3611                            )
3612                            .is_none()
3613                        {
3614                            return CheckListSetPollRet::RemoveSocket {
3615                                checklist_id,
3616                                // FIXME; hardcoded component id
3617                                component_id: 1,
3618                                transport,
3619                                local_addr: client.local_addr(),
3620                                remote_addr: client.remote_addr(),
3621                            };
3622                        }
3623                        continue;
3624                    }
3625                    TurnPollRet::WaitUntil(wait) => {
3626                        all_turn_closed = false;
3627                        if wait == now {
3628                            return CheckListSetPollRet::WaitUntil(
3629                                wait.max(
3630                                    self.last_send_time
3631                                        .map(|last_send| last_send + self.timing_advance)
3632                                        .unwrap_or(now),
3633                                ),
3634                            );
3635                        }
3636                        if wait < lowest_wait {
3637                            lowest_wait = wait.max(
3638                                self.last_send_time
3639                                    .map(|last_send| last_send + self.timing_advance)
3640                                    .unwrap_or(now),
3641                            );
3642                        }
3643                    }
3644                    TurnPollRet::TcpClose {
3645                        local_addr,
3646                        remote_addr,
3647                    } => {
3648                        return CheckListSetPollRet::RemoveSocket {
3649                            checklist_id: checklist.checklist_id,
3650                            // FIXME: hardcoded component
3651                            component_id: 1,
3652                            transport: client.client.transport(),
3653                            local_addr,
3654                            remote_addr,
3655                        };
3656                    }
3657                    // ignoring allocation for closing TURN
3658                    TurnPollRet::AllocateTcpSocket {
3659                        id: _,
3660                        socket: _,
3661                        peer_addr: _,
3662                    } => continue,
3663                }
3664                idx += 1;
3665            }
3666
3667            if let Some(event) = checklist.poll_event() {
3668                if matches!(event, ConnCheckEvent::SelectedPair(_, _)) {
3669                    self.maybe_remove_sockets(self.checklist_i, now);
3670                }
3671                return CheckListSetPollRet::Event {
3672                    checklist_id,
3673                    event,
3674                };
3675            }
3676
3677            let checklist_state = checklist.state();
3678            if checklist_state == CheckListState::Running {
3679                n_running += 1;
3680                for idx in 0..checklist.pairs.len() {
3681                    let check = &mut checklist.pairs[idx];
3682                    if check.state != CandidatePairState::InProgress {
3683                        continue;
3684                    }
3685                    let conncheck_id = check.conncheck_id;
3686                    if let Some(agent_id) = check.agent_id() {
3687                        if let Some(agent) = checklist.mut_agent_by_id(agent_id) {
3688                            trace!("polling existing stun request for check {conncheck_id}");
3689                            match agent.poll(now) {
3690                                StunAgentPollRet::TransactionTimedOut(_request) => {
3691                                    checklist.check_cancel_retransmissions(conncheck_id);
3692                                    let check = &mut checklist.pairs[idx];
3693                                    check.set_state(CandidatePairState::Failed);
3694                                }
3695                                StunAgentPollRet::TransactionCancelled(_request) => {
3696                                    checklist.check_cancel_retransmissions(conncheck_id);
3697                                    let check = &mut checklist.pairs[idx];
3698                                    check.set_state(CandidatePairState::Failed);
3699                                }
3700                                StunAgentPollRet::WaitUntil(wait) => {
3701                                    if wait < lowest_wait {
3702                                        trace!("stun poll ret {wait:?}");
3703                                        lowest_wait = wait.max(
3704                                            self.last_send_time
3705                                                .map(|last_send| last_send + self.timing_advance)
3706                                                .unwrap_or(now),
3707                                        );
3708                                    }
3709                                }
3710                            }
3711                        } else if let Some(client) = checklist.mut_turn_client_by_id(agent_id) {
3712                            match client.poll(now) {
3713                                TurnPollRet::WaitUntil(wait) => {
3714                                    if wait < lowest_wait {
3715                                        lowest_wait = wait.max(
3716                                            self.last_send_time
3717                                                .map(|last_send| last_send + self.timing_advance)
3718                                                .unwrap_or(now),
3719                                        );
3720                                    }
3721                                }
3722                                TurnPollRet::Closed => (),
3723                                TurnPollRet::TcpClose {
3724                                    local_addr,
3725                                    remote_addr,
3726                                } => {
3727                                    return CheckListSetPollRet::RemoveSocket {
3728                                        checklist_id,
3729                                        // FIXME: hardcoded component
3730                                        component_id: 1,
3731                                        transport: client.transport(),
3732                                        local_addr,
3733                                        remote_addr,
3734                                    };
3735                                }
3736                                TurnPollRet::AllocateTcpSocket {
3737                                    id: _,
3738                                    socket: _,
3739                                    peer_addr: _,
3740                                } => unimplemented!(),
3741                            }
3742                        } else {
3743                            unreachable!();
3744                        }
3745                    }
3746                }
3747                trace!("last send time {:?}", self.last_send_time);
3748                if let Some(last_send) = self.last_send_time {
3749                    if last_send + self.timing_advance > now {
3750                        trace!("last send time too close to the past, waiting");
3751                        return CheckListSetPollRet::WaitUntil(last_send + self.timing_advance);
3752                    }
3753                }
3754            }
3755            checklist.check_for_failure();
3756            trace!(
3757                "checklist id:{} state: {:?}",
3758                checklist_id,
3759                checklist.state()
3760            );
3761            if checklist.state() == CheckListState::Failed {
3762                if checklist_state == CheckListState::Running {
3763                    n_running -= 1;
3764                }
3765            } else {
3766                all_failed = false;
3767            }
3768
3769            let conncheck_id = match self.next_check() {
3770                Some(c) => c,
3771                None => {
3772                    if start_idx == self.checklist_i {
3773                        debug!(
3774                            "nothing to do yet n-running:{n_running} completed:{} all-failed:{all_failed} turn-closed:{all_turn_closed}",
3775                            self.completed
3776                        );
3777                        // we looked at them all and none of the checklist could find anything to
3778                        // do
3779                        if n_running == 0 && !self.completed {
3780                            self.completed = true;
3781                            return CheckListSetPollRet::Completed;
3782                        } else if let Some(remove) = self.pending_remove_sockets.pop_front() {
3783                            return remove.into_remove();
3784                        } else if all_failed && all_turn_closed {
3785                            return CheckListSetPollRet::Closed;
3786                        } else {
3787                            return CheckListSetPollRet::WaitUntil(lowest_wait);
3788                        }
3789                    } else {
3790                        continue;
3791                    }
3792                }
3793            };
3794
3795            trace!("starting conncheck");
3796            match self.perform_conncheck(self.checklist_i, conncheck_id, now) {
3797                Ok(Some(socket)) => return socket.into_allocate(),
3798                Ok(None) => {
3799                    let checklist = &mut self.checklists[self.checklist_i];
3800                    if let Some(event) = checklist.poll_event() {
3801                        let checklist_id = checklist.checklist_id;
3802                        if matches!(event, ConnCheckEvent::SelectedPair(_, _)) {
3803                            self.maybe_remove_sockets(self.checklist_i, now);
3804                        }
3805                        return CheckListSetPollRet::Event {
3806                            checklist_id,
3807                            event,
3808                        };
3809                    } else {
3810                        return CheckListSetPollRet::WaitUntil(now);
3811                    }
3812                }
3813                Err(e) => warn!("failed to perform check: {e:?}"),
3814            }
3815        }
3816    }
3817
3818    #[tracing::instrument(name = "check_set_poll_transmit", level = "trace", skip(self))]
3819    pub fn poll_transmit(&mut self, now: Instant) -> Option<CheckListSetTransmit> {
3820        if let Some(pending) = self.pending_transmits.pop_back() {
3821            trace!(
3822                "pending {} {} -> {} transmit of {} bytes",
3823                pending.transmit.transport,
3824                pending.transmit.from,
3825                pending.transmit.to,
3826                pending.transmit.data.len()
3827            );
3828            return Some(pending);
3829        }
3830
3831        let rto = self.rto.clone();
3832        while let Some(pending) = self.pending_messages.pop_back() {
3833            let Some(checklist) = self.mut_list(pending.checklist_id) else {
3834                continue;
3835            };
3836            let Some(agent) = checklist.mut_agent_by_id(pending.agent_id) else {
3837                continue;
3838            };
3839            if pending.is_request {
3840                let hdr = MessageHeader::from_bytes(&pending.msg).unwrap();
3841                debug!(
3842                    "Sending request {hdr:?} to {:?} using agent: {:?} and turn id {:?}",
3843                    pending.to, pending.agent_id, pending.turn_id
3844                );
3845                match agent.send_request(pending.msg, pending.to, now) {
3846                    Ok(transmit) => {
3847                        let transport = transmit.transport;
3848                        let transmit =
3849                            transmit.reinterpret_data(|data| transmit_send(transport, data));
3850                        if let Some(rto) = rto.as_ref() {
3851                            let mut request = agent
3852                                .mut_request_transaction(hdr.transaction_id())
3853                                .expect("Just sent request does not exist!");
3854                            request.configure_timeout_with_max(
3855                                rto.initial,
3856                                rto.retransmits,
3857                                rto.final_retransmit_timeout,
3858                                rto.max,
3859                            );
3860                        }
3861                        if let Some((turn_id, _turn_to)) = pending.turn_id {
3862                            let transport = transmit.transport;
3863                            let Some(client) = checklist.mut_turn_client_by_id(turn_id) else {
3864                                continue;
3865                            };
3866                            match client.send_to(transport, pending.to, transmit.data, now) {
3867                                Ok(transmit) => {
3868                                    if let Some(transmit) = transmit {
3869                                        self.last_send_time = Some(now);
3870                                        return Some(CheckListSetTransmit {
3871                                            checklist_id: pending.checklist_id,
3872                                            transmit: transmit_send_build_unframed(transmit),
3873                                        });
3874                                    }
3875                                }
3876                                Err(e) => warn!("error sending: {e}"),
3877                            }
3878                        } else {
3879                            self.last_send_time = Some(now);
3880                            return Some(CheckListSetTransmit {
3881                                checklist_id: pending.checklist_id,
3882                                transmit,
3883                            });
3884                        }
3885                    }
3886                    Err(e) => warn!("error sending: {e}"),
3887                }
3888            } else {
3889                debug!("Sending response {:?} to {:?}", pending.msg, pending.to);
3890                match agent.send(pending.msg, pending.to, now) {
3891                    Ok(transmit) => {
3892                        let transport = transmit.transport;
3893                        let transmit =
3894                            transmit.reinterpret_data(|data| transmit_send(transport, data));
3895                        if let Some((turn_id, _turn_to)) = pending.turn_id {
3896                            let Some(client) = checklist.mut_turn_client_by_id(turn_id) else {
3897                                continue;
3898                            };
3899                            match client.send_to(transport, pending.to, transmit.data, now) {
3900                                Ok(transmit) => {
3901                                    if let Some(transmit) = transmit {
3902                                        self.last_send_time = Some(now);
3903                                        return Some(CheckListSetTransmit {
3904                                            checklist_id: pending.checklist_id,
3905                                            transmit: transmit_send_build_unframed(transmit),
3906                                        });
3907                                    }
3908                                }
3909                                Err(e) => warn!("error sending: {e}"),
3910                            }
3911                        } else {
3912                            self.last_send_time = Some(now);
3913                            return Some(CheckListSetTransmit {
3914                                checklist_id: pending.checklist_id,
3915                                transmit,
3916                            });
3917                        }
3918                    }
3919                    Err(e) => warn!("error sending: {e}"),
3920                }
3921            }
3922        }
3923
3924        if self
3925            .last_send_time
3926            .is_some_and(|last_send| last_send + self.timing_advance > now)
3927        {
3928            return None;
3929        }
3930
3931        for checklist_i in 0..self.checklists.len() {
3932            let checklist = &self.checklists[checklist_i];
3933            let checklist_id = checklist.checklist_id;
3934            let agents_len = self.checklists[checklist_i].agents.len();
3935            for check_agent_i in 0..agents_len {
3936                let checklist = &mut self.checklists[checklist_i];
3937                let turn_id = checklist.agents[check_agent_i].turn_id;
3938                if let Some(turn_id) = turn_id {
3939                    let check_agent = &mut checklist.agents[check_agent_i];
3940                    let Some(transmit) = check_agent.agent.poll_transmit(now) else {
3941                        continue;
3942                    };
3943                    let transmit = transmit.reinterpret_data(|data| data.to_vec());
3944
3945                    let transport = transmit.transport;
3946                    let client = checklist.mut_turn_client_by_id(turn_id).unwrap();
3947                    match client.send_to(transport, transmit.to, transmit.data, now) {
3948                        Ok(transmit) => {
3949                            if let Some(transmit) = transmit {
3950                                self.last_send_time = Some(now);
3951                                return Some(CheckListSetTransmit {
3952                                    checklist_id,
3953                                    transmit: transmit_send_build_unframed(transmit),
3954                                });
3955                            }
3956                        }
3957                        Err(e) => warn!("error sending: {e}"),
3958                    }
3959                } else {
3960                    let check_agent = &mut checklist.agents[check_agent_i];
3961                    let Some(transmit) = check_agent.agent.poll_transmit(now) else {
3962                        continue;
3963                    };
3964
3965                    let transport = transmit.transport;
3966                    self.last_send_time = Some(now);
3967                    return Some(CheckListSetTransmit {
3968                        checklist_id,
3969                        transmit: transmit.reinterpret_data(|data| transmit_send(transport, data)),
3970                    });
3971                }
3972            }
3973
3974            let checklist = &mut self.checklists[checklist_i];
3975            for client in checklist.turn_clients.iter_mut() {
3976                let Some(transmit) = client.client.poll_transmit(now) else {
3977                    continue;
3978                };
3979
3980                self.last_send_time = Some(now);
3981                return Some(CheckListSetTransmit {
3982                    checklist_id,
3983                    transmit: transmit_send_unframed(transmit),
3984                });
3985            }
3986
3987            for client in checklist.pending_delete_turn_clients.iter_mut() {
3988                let Some(transmit) = client.client.poll_transmit(now) else {
3989                    continue;
3990                };
3991
3992                self.last_send_time = Some(now);
3993                return Some(CheckListSetTransmit {
3994                    checklist_id,
3995                    transmit: transmit_send_unframed(transmit),
3996                });
3997            }
3998        }
3999        None
4000    }
4001
4002    /// Report a reply (success or failure) to a TCP connection attempt.
4003    /// [`ConnCheckListSet::poll`] should be called at the earliest opportunity.
4004    #[tracing::instrument(
4005        level = "debug",
4006        skip(self, checklist_id, component_id),
4007        fields(
4008            checklist.id = checklist_id,
4009            component.id = component_id,
4010            ?local_addr,
4011        )
4012    )]
4013    #[allow(clippy::too_many_arguments)]
4014    pub fn allocated_socket(
4015        &mut self,
4016        checklist_id: usize,
4017        component_id: usize,
4018        transport: TransportType,
4019        from: SocketAddr,
4020        to: SocketAddr,
4021        local_addr: Result<SocketAddr, StunError>,
4022        now: Instant,
4023    ) {
4024        let Some(checklist) = self
4025            .checklists
4026            .iter_mut()
4027            .find(|checklist| checklist.checklist_id == checklist_id)
4028        else {
4029            debug!("no checklist with id {checklist_id}");
4030            return;
4031        };
4032
4033        debug!(
4034            "pending TURN TCP sockets: {:?}",
4035            checklist.pending_turn_tcp_connect
4036        );
4037        if let Some(pending) = checklist
4038            .pending_turn_tcp_connect
4039            .iter_mut()
4040            .find(|pending| {
4041                transport == TransportType::Tcp
4042                    && pending.turn_addr == to
4043                    && pending.turn_connect_id.is_some()
4044                    && !pending.allocated
4045            })
4046        {
4047            debug!("allocated TURN TCP socket");
4048            pending.allocated = true;
4049            let Some(client) = checklist.turn_clients.iter_mut().find_map(|client| {
4050                if pending.turn_id == client.id {
4051                    Some(client)
4052                } else {
4053                    None
4054                }
4055            }) else {
4056                warn!("no TURN client for allocated socket");
4057                return;
4058            };
4059            let local_addr = local_addr.ok();
4060            client
4061                .client
4062                .allocated_tcp_socket(
4063                    pending.turn_connect_id.unwrap(),
4064                    Socket5Tuple {
4065                        transport: TransportType::Tcp,
4066                        from,
4067                        to,
4068                    },
4069                    pending.peer_addr,
4070                    local_addr,
4071                    now,
4072                )
4073                .unwrap();
4074            if let Some(local_addr) = local_addr {
4075                client.local_tcp_sockets.push(local_addr);
4076            }
4077            return;
4078        }
4079        // FIXME: handle TURN TCP connection construction
4080
4081        if checklist.agents.iter().map(|a| &a.agent).any(|a| {
4082            a.transport() == TransportType::Tcp
4083                && a.local_addr() == from
4084                && a.remote_addr() == Some(to)
4085        }) {
4086            panic!("Adding an agent with the same 5-tuple multiple times is not supported");
4087        }
4088
4089        for check in checklist.pairs.iter_mut() {
4090            if check.pair.local.transport_type != TransportType::Tcp {
4091                continue;
4092            }
4093            if check.pair.remote.address != to {
4094                continue;
4095            }
4096            // FIXME: handle TURN TCP connection construction
4097            if from != check.pair.local.base_address {
4098                continue;
4099            }
4100            if check.stun_request.is_some() {
4101                continue;
4102            }
4103            if check.pair.local.component_id != component_id {
4104                continue;
4105            }
4106            if check.state != CandidatePairState::InProgress {
4107                continue;
4108            }
4109            trace!("found check with id {} to set agent", check.conncheck_id);
4110            match local_addr {
4111                Ok(local_addr) => {
4112                    let mut new_pair = check.pair.clone();
4113                    let agent = StunAgent::builder(transport, local_addr)
4114                        .remote_addr(check.pair.remote.address)
4115                        .build();
4116                    // FIXME: handle TURN TCP connection construction
4117                    new_pair.local.base_address = local_addr;
4118                    new_pair.local.address = local_addr;
4119
4120                    let Ok(stun_request) = ConnCheck::generate_stun_request(
4121                        &new_pair,
4122                        check.nominate,
4123                        self.controlling,
4124                        self.tie_breaker,
4125                        checklist.local_credentials.clone(),
4126                        checklist.remote_credentials.clone(),
4127                    ) else {
4128                        warn!("failed to generate stun request for new tcp agent");
4129                        return;
4130                    };
4131                    let transaction_id = stun_request.transaction_id();
4132
4133                    let checklist_id = check.checklist_id;
4134                    let nominate = check.nominate;
4135                    let conncheck_id = check.conncheck_id;
4136
4137                    // FIXME: TURN-TCP
4138                    let (agent_id, _agent_idx) = checklist.add_agent(agent, None);
4139                    self.pending_messages
4140                        .push_front(CheckListSetPendingMessage {
4141                            checklist_id,
4142                            agent_id,
4143                            is_request: true,
4144                            msg: stun_request.finish(),
4145                            to,
4146                            turn_id: None,
4147                        });
4148
4149                    let mut new_check = ConnCheck::new(
4150                        checklist_id,
4151                        new_pair.clone(),
4152                        agent_id,
4153                        nominate,
4154                        self.controlling,
4155                        checklist.remote_credentials.clone(),
4156                    );
4157                    let is_triggered = checklist
4158                        .triggered
4159                        .iter()
4160                        .any(|&check_id| conncheck_id == check_id);
4161                    new_check.set_state(CandidatePairState::InProgress);
4162                    new_check.stun_request = Some(transaction_id);
4163
4164                    let old_conncheck_id = conncheck_id;
4165                    checklist
4166                        .pairs
4167                        .retain(|check| check.conncheck_id != old_conncheck_id);
4168                    checklist
4169                        .triggered
4170                        .retain(|&check_id| check_id != old_conncheck_id);
4171                    checklist
4172                        .valid
4173                        .retain(|&check_id| check_id != old_conncheck_id);
4174                    if is_triggered {
4175                        checklist.add_triggered(&new_check);
4176                    }
4177                    checklist.add_check(new_check);
4178                }
4179                Err(_e) => {
4180                    check.set_state(CandidatePairState::Failed);
4181                }
4182            }
4183            break;
4184        }
4185    }
4186
4187    pub fn close(&mut self, now: Instant) {
4188        for checklist_i in 0..self.checklists.len() {
4189            let checklist = &mut self.checklists[checklist_i];
4190            let mut checks = VecDeque::new();
4191            core::mem::swap(&mut checks, &mut checklist.pairs);
4192            for check in checks {
4193                self.remove_check_resources(checklist_i, check, now);
4194            }
4195            let checklist = &mut self.checklists[checklist_i];
4196            let mut turn_clients = Vec::new();
4197            core::mem::swap(&mut turn_clients, &mut checklist.turn_clients);
4198            for mut client in turn_clients {
4199                let _ = client.client.delete(now);
4200                checklist.pending_delete_turn_clients.push(client);
4201            }
4202            checklist.close();
4203        }
4204        self.closed = true;
4205    }
4206}
4207
4208/// Return values for polling a set of checklists.
4209#[derive(Debug)]
4210pub enum CheckListSetPollRet {
4211    /// The check lists are closed for any processing.
4212    Closed,
4213    /// Allocate a socket using the specified network 5-tuple.  Report success
4214    /// or failure with `allocated_socket()`.
4215    AllocateSocket {
4216        checklist_id: usize,
4217        component_id: usize,
4218        transport: TransportType,
4219        local_addr: SocketAddr,
4220        remote_addr: SocketAddr,
4221    },
4222    /// Remove a socket using the specified network 5-tuple. The socket will not be referenced
4223    /// further.
4224    RemoveSocket {
4225        checklist_id: usize,
4226        component_id: usize,
4227        transport: TransportType,
4228        local_addr: SocketAddr,
4229        remote_addr: SocketAddr,
4230    },
4231    /// Wait until the specified time has passed.  Receiving handled data may cause a different
4232    /// value to be returned from `poll()`
4233    WaitUntil(Instant),
4234    /// An event has occured
4235    Event {
4236        checklist_id: usize,
4237        event: ConnCheckEvent,
4238    },
4239    /// The set has completed all operations and has either succeeded or failed.  Further progress
4240    /// will not be made.
4241    Completed,
4242}
4243
4244#[derive(Debug)]
4245pub struct CheckListSetTransmit {
4246    pub checklist_id: usize,
4247    pub transmit: Transmit<Box<[u8]>>,
4248}
4249
4250#[derive(Debug)]
4251struct CheckListSetPendingMessage {
4252    checklist_id: usize,
4253    agent_id: StunAgentId,
4254    is_request: bool,
4255    msg: Vec<u8>,
4256    turn_id: Option<(StunAgentId, SocketAddr)>,
4257    to: SocketAddr,
4258}
4259
4260#[derive(Debug)]
4261struct CheckListSetSocket {
4262    checklist_id: usize,
4263    component_id: usize,
4264    transport: TransportType,
4265    local_addr: SocketAddr,
4266    remote_addr: SocketAddr,
4267}
4268
4269impl CheckListSetSocket {
4270    fn into_allocate(self) -> CheckListSetPollRet {
4271        CheckListSetPollRet::AllocateSocket {
4272            checklist_id: self.checklist_id,
4273            component_id: self.component_id,
4274            transport: self.transport,
4275            local_addr: self.local_addr,
4276            remote_addr: self.remote_addr,
4277        }
4278    }
4279
4280    fn into_remove(self) -> CheckListSetPollRet {
4281        CheckListSetPollRet::RemoveSocket {
4282            checklist_id: self.checklist_id,
4283            component_id: self.component_id,
4284            transport: self.transport,
4285            local_addr: self.local_addr,
4286            remote_addr: self.remote_addr,
4287        }
4288    }
4289}
4290
4291#[derive(Debug)]
4292pub struct PendingRecv {
4293    pub component_id: usize,
4294    pub data: Vec<u8>,
4295}
4296
4297fn pair_tcp_type(local: TcpType) -> TcpType {
4298    match local {
4299        TcpType::Active => TcpType::Passive,
4300        TcpType::Passive => TcpType::Active,
4301        TcpType::So => TcpType::So,
4302    }
4303}
4304
4305fn pair_construct_valid(pair: &CandidatePair, mapped_address: SocketAddr) -> CandidatePair {
4306    let mut local = pair.local.clone();
4307    local.address = mapped_address;
4308    CandidatePair {
4309        local,
4310        remote: pair.remote.clone(),
4311    }
4312}
4313
4314// can the local candidate pair with 'remote' in any way
4315fn candidate_can_pair_with(local: &Candidate, remote: &Candidate) -> bool {
4316    debug!("local: {local:?}");
4317    debug!("remote: {remote:?}");
4318    if local.transport_type == TransportType::Tcp
4319        && remote.transport_type == TransportType::Tcp
4320        && (local.tcp_type.is_none()
4321            || remote.tcp_type.is_none()
4322            || pair_tcp_type(local.tcp_type.unwrap()) != remote.tcp_type.unwrap())
4323    {
4324        return false;
4325    }
4326    local.transport_type == remote.transport_type
4327        && local.component_id == remote.component_id
4328        && local.base_address.is_ipv4() == remote.address.is_ipv4()
4329        && local.base_address.is_ipv6() == remote.address.is_ipv6()
4330}
4331
4332fn validate_username(username: Username, local_credentials: &Credentials) -> bool {
4333    let username = username.username().as_bytes();
4334    let local_user = local_credentials.ufrag.as_bytes();
4335    if local_user.len()
4336        == local_user
4337            .iter()
4338            .zip(username)
4339            .take_while(|(l, r)| l == r)
4340            .count()
4341    {
4342        true
4343    } else {
4344        debug!("binding request failed username validation");
4345        false
4346    }
4347}
4348
4349pub(crate) fn transmit_send<T: AsRef<[u8]>>(transport: TransportType, data: T) -> Box<[u8]> {
4350    match transport {
4351        TransportType::Udp => data.as_ref().into(),
4352        TransportType::Tcp => {
4353            let len = data.as_ref().len();
4354            let mut ret = Vec::with_capacity(2 + len);
4355            ret.resize(2, 0);
4356            BigEndian::write_u16(&mut ret, len as u16);
4357            ret.extend_from_slice(data.as_ref());
4358            ret.into_boxed_slice()
4359        }
4360    }
4361}
4362
4363fn transmit_send_build_unframed<T: DelayedTransmitBuild + core::fmt::Debug>(
4364    transmit: TransmitBuild<T>,
4365) -> Transmit<Box<[u8]>> {
4366    Transmit::new(
4367        transmit.data.build().into_boxed_slice(),
4368        transmit.transport,
4369        transmit.from,
4370        transmit.to,
4371    )
4372}
4373
4374fn transmit_send_unframed(transmit: Transmit<Data<'_>>) -> Transmit<Box<[u8]>> {
4375    transmit.reinterpret_data(|data| match data {
4376        Data::Owned(owned) => owned.take(),
4377        Data::Borrowed(borrowed) => borrowed.take().into(),
4378    })
4379}
4380
4381fn new_agent(transport: TransportType, local: SocketAddr, remote: SocketAddr) -> StunAgent {
4382    let mut agent = StunAgent::builder(transport, local);
4383    if transport == TransportType::Tcp {
4384        agent = agent.remote_addr(remote);
4385    }
4386    agent.build()
4387}
4388
4389#[cfg(test)]
4390mod tests {
4391    use alloc::borrow::ToOwned;
4392    use alloc::vec;
4393
4394    use turn_client_proto::{
4395        tcp::TurnClientTcp,
4396        types::{
4397            TurnCredentials,
4398            message::{ALLOCATE, CONNECT, CONNECTION_BIND, CREATE_PERMISSION},
4399        },
4400        udp::TurnClientUdp,
4401    };
4402    use turn_server_proto::api::{TurnServerApi, TurnServerPollRet};
4403    use turn_server_proto::server::TurnServer;
4404
4405    use super::*;
4406    use crate::candidate::*;
4407
4408    #[test]
4409    fn nominate_eq_bool() {
4410        let _log = crate::tests::test_init_log();
4411        assert!(Nominate::DontCare.eq(&true));
4412        assert!(Nominate::DontCare.eq(&false));
4413        assert!(Nominate::True.eq(&true));
4414        assert!(Nominate::False.eq(&false));
4415        assert!(!Nominate::False.eq(&true));
4416        assert!(!Nominate::True.eq(&false));
4417    }
4418
4419    #[test]
4420    fn nominate_eq_nominate() {
4421        let _log = crate::tests::test_init_log();
4422        assert!(Nominate::DontCare.eq(&Nominate::DontCare));
4423        assert!(Nominate::DontCare.eq(&Nominate::True));
4424        assert!(Nominate::DontCare.eq(&Nominate::False));
4425        assert!(Nominate::True.eq(&Nominate::DontCare));
4426        assert!(Nominate::False.eq(&Nominate::DontCare));
4427        assert!(Nominate::True.eq(&Nominate::True));
4428        assert!(Nominate::False.eq(&Nominate::False));
4429        assert!(!Nominate::True.eq(&Nominate::False));
4430        assert!(!Nominate::False.eq(&Nominate::True));
4431    }
4432
4433    #[test]
4434    fn candidate_pair_ordering() {
4435        assert!(
4436            CandidatePairState::Failed
4437                .cmp_progression(&CandidatePairState::Failed)
4438                .is_eq()
4439        );
4440        assert!(
4441            CandidatePairState::Failed
4442                .cmp_progression(&CandidatePairState::Frozen)
4443                .is_lt()
4444        );
4445        assert!(
4446            CandidatePairState::Failed
4447                .cmp_progression(&CandidatePairState::Waiting)
4448                .is_lt()
4449        );
4450        assert!(
4451            CandidatePairState::Failed
4452                .cmp_progression(&CandidatePairState::InProgress)
4453                .is_lt()
4454        );
4455        assert!(
4456            CandidatePairState::Failed
4457                .cmp_progression(&CandidatePairState::Succeeded)
4458                .is_lt()
4459        );
4460
4461        assert!(
4462            CandidatePairState::Frozen
4463                .cmp_progression(&CandidatePairState::Failed)
4464                .is_gt()
4465        );
4466        assert!(
4467            CandidatePairState::Frozen
4468                .cmp_progression(&CandidatePairState::Frozen)
4469                .is_eq()
4470        );
4471        assert!(
4472            CandidatePairState::Frozen
4473                .cmp_progression(&CandidatePairState::Waiting)
4474                .is_lt()
4475        );
4476        assert!(
4477            CandidatePairState::Frozen
4478                .cmp_progression(&CandidatePairState::InProgress)
4479                .is_lt()
4480        );
4481        assert!(
4482            CandidatePairState::Frozen
4483                .cmp_progression(&CandidatePairState::Succeeded)
4484                .is_lt()
4485        );
4486
4487        assert!(
4488            CandidatePairState::Waiting
4489                .cmp_progression(&CandidatePairState::Failed)
4490                .is_gt()
4491        );
4492        assert!(
4493            CandidatePairState::Waiting
4494                .cmp_progression(&CandidatePairState::Frozen)
4495                .is_gt()
4496        );
4497        assert!(
4498            CandidatePairState::Waiting
4499                .cmp_progression(&CandidatePairState::Waiting)
4500                .is_eq()
4501        );
4502        assert!(
4503            CandidatePairState::Waiting
4504                .cmp_progression(&CandidatePairState::InProgress)
4505                .is_lt()
4506        );
4507        assert!(
4508            CandidatePairState::Waiting
4509                .cmp_progression(&CandidatePairState::Succeeded)
4510                .is_lt()
4511        );
4512
4513        assert!(
4514            CandidatePairState::InProgress
4515                .cmp_progression(&CandidatePairState::Failed)
4516                .is_gt()
4517        );
4518        assert!(
4519            CandidatePairState::InProgress
4520                .cmp_progression(&CandidatePairState::Frozen)
4521                .is_gt()
4522        );
4523        assert!(
4524            CandidatePairState::InProgress
4525                .cmp_progression(&CandidatePairState::Waiting)
4526                .is_gt()
4527        );
4528        assert!(
4529            CandidatePairState::InProgress
4530                .cmp_progression(&CandidatePairState::InProgress)
4531                .is_eq()
4532        );
4533        assert!(
4534            CandidatePairState::InProgress
4535                .cmp_progression(&CandidatePairState::Succeeded)
4536                .is_lt()
4537        );
4538
4539        assert!(
4540            CandidatePairState::Succeeded
4541                .cmp_progression(&CandidatePairState::Failed)
4542                .is_gt()
4543        );
4544        assert!(
4545            CandidatePairState::Succeeded
4546                .cmp_progression(&CandidatePairState::Frozen)
4547                .is_gt()
4548        );
4549        assert!(
4550            CandidatePairState::Succeeded
4551                .cmp_progression(&CandidatePairState::Waiting)
4552                .is_gt()
4553        );
4554        assert!(
4555            CandidatePairState::Succeeded
4556                .cmp_progression(&CandidatePairState::InProgress)
4557                .is_gt()
4558        );
4559        assert!(
4560            CandidatePairState::Succeeded
4561                .cmp_progression(&CandidatePairState::Succeeded)
4562                .is_eq()
4563        );
4564    }
4565
4566    #[derive(Debug)]
4567    struct Peer {
4568        candidate: Candidate,
4569        local_credentials: Option<Credentials>,
4570        remote_credentials: Option<Credentials>,
4571    }
4572
4573    impl Peer {
4574        fn default() -> Self {
4575            Peer::builder().build()
4576        }
4577
4578        fn builder() -> PeerBuilder {
4579            PeerBuilder::default()
4580        }
4581
4582        fn stun_agent_with_remote(
4583            &self,
4584            remote_addr: SocketAddr,
4585        ) -> (StunAgent, ShortTermAuth, ShortTermAuth) {
4586            let mut agent =
4587                StunAgent::builder(self.candidate.transport_type, self.candidate.base_address);
4588            if self.candidate.transport_type == TransportType::Tcp {
4589                agent = agent.remote_addr(remote_addr)
4590            }
4591            let agent = agent.build();
4592            let mut local_auth = ShortTermAuth::new();
4593            let mut remote_auth = ShortTermAuth::new();
4594            self.configure_stun_auth(&mut local_auth, &mut remote_auth);
4595            (agent, local_auth, remote_auth)
4596        }
4597
4598        fn stun_agent(&self) -> (StunAgent, ShortTermAuth, ShortTermAuth) {
4599            if self.candidate.transport_type == TransportType::Tcp {
4600                unreachable!();
4601            }
4602            let agent =
4603                StunAgent::builder(self.candidate.transport_type, self.candidate.base_address);
4604            let agent = agent.build();
4605            let mut local_auth = ShortTermAuth::new();
4606            let mut remote_auth = ShortTermAuth::new();
4607            self.configure_stun_auth(&mut local_auth, &mut remote_auth);
4608            (agent, local_auth, remote_auth)
4609        }
4610
4611        fn configure_stun_auth(
4612            &self,
4613            local_auth: &mut ShortTermAuth,
4614            remote_auth: &mut ShortTermAuth,
4615        ) {
4616            let local_credentials = self
4617                .local_credentials
4618                .clone()
4619                .unwrap_or_else(|| Credentials::new(String::from("user"), String::from("pass")));
4620            local_auth.set_credentials(local_credentials.into(), IntegrityAlgorithm::Sha1);
4621            if let Some(remote_credentials) = self.remote_credentials.as_ref() {
4622                remote_auth
4623                    .set_credentials(remote_credentials.clone().into(), IntegrityAlgorithm::Sha1);
4624            }
4625        }
4626    }
4627
4628    #[derive(Debug, Default)]
4629    struct PeerBuilder {
4630        foundation: Option<String>,
4631        local_credentials: Option<Credentials>,
4632        remote_credentials: Option<Credentials>,
4633        component_id: Option<usize>,
4634        priority: Option<u32>,
4635        transport: Option<TransportType>,
4636        local_addr: Option<SocketAddr>,
4637        candidate: Option<Candidate>,
4638        tcp_type: Option<TcpType>,
4639    }
4640
4641    impl PeerBuilder {
4642        fn foundation(mut self, foundation: &str) -> Self {
4643            self.foundation = Some(foundation.to_owned());
4644            self
4645        }
4646
4647        fn local_credentials(mut self, credentials: Credentials) -> Self {
4648            self.local_credentials = Some(credentials);
4649            self
4650        }
4651
4652        fn remote_credentials(mut self, credentials: Credentials) -> Self {
4653            self.remote_credentials = Some(credentials);
4654            self
4655        }
4656
4657        fn component_id(mut self, component_id: usize) -> Self {
4658            self.component_id = Some(component_id);
4659            self
4660        }
4661
4662        fn priority(mut self, priority: u32) -> Self {
4663            self.priority = Some(priority);
4664            self
4665        }
4666
4667        fn transport(mut self, transport: TransportType) -> Self {
4668            self.transport = Some(transport);
4669            self
4670        }
4671
4672        fn tcp_type(mut self, tcp_type: TcpType) -> Self {
4673            self.tcp_type = Some(tcp_type);
4674            self
4675        }
4676
4677        fn local_addr(mut self, addr: SocketAddr) -> Self {
4678            self.local_addr = Some(addr);
4679            self
4680        }
4681
4682        fn build(self) -> Peer {
4683            let addr = self.candidate.as_ref().map(|c| c.base_address).unwrap_or(
4684                self.local_addr
4685                    .unwrap_or_else(|| "127.0.0.1:0".parse().unwrap()),
4686            );
4687            let ttype = self
4688                .candidate
4689                .as_ref()
4690                .map(|c| c.transport_type)
4691                .unwrap_or(self.transport.unwrap_or(TransportType::Udp));
4692
4693            let tcp_type = match ttype {
4694                TransportType::Udp => {
4695                    assert!(self.tcp_type.is_none());
4696                    None
4697                }
4698                TransportType::Tcp => Some(self.tcp_type.unwrap_or(TcpType::Passive)),
4699            };
4700
4701            if let Some(candidate) = &self.candidate {
4702                if let Some(component_id) = self.component_id {
4703                    if component_id != candidate.component_id {
4704                        panic!("mismatched component ids");
4705                    }
4706                }
4707                if let Some(foundation) = self.foundation.clone() {
4708                    if foundation != candidate.foundation {
4709                        panic!("mismatched foundations");
4710                    }
4711                }
4712            }
4713            let candidate = self.candidate.unwrap_or_else(|| {
4714                let mut builder = Candidate::builder(
4715                    self.component_id.unwrap_or(1),
4716                    CandidateType::Host,
4717                    ttype,
4718                    &self.foundation.unwrap_or(String::from("0")),
4719                    addr,
4720                );
4721                if let Some(priority) = self.priority {
4722                    builder = builder.priority(priority);
4723                }
4724                if let Some(tcp_type) = tcp_type {
4725                    builder = builder.tcp_type(tcp_type);
4726                }
4727                builder.build()
4728            });
4729
4730            Peer {
4731                candidate,
4732                local_credentials: self.local_credentials,
4733                remote_credentials: self.remote_credentials,
4734            }
4735        }
4736    }
4737
4738    #[test]
4739    fn get_candidates() {
4740        let _log = crate::tests::test_init_log();
4741        let mut set = ConnCheckListSet::builder(0, true).build();
4742        let list = set.new_list();
4743        let list = set.mut_list(list).unwrap();
4744        list.add_component(1);
4745
4746        let local = Peer::default();
4747        let remote = Peer::default();
4748
4749        list.add_local_candidate(local.candidate.clone());
4750        list.add_remote_candidate(remote.candidate.clone());
4751
4752        // The candidate list is only what we put in
4753        let mut locals = list.local_candidates();
4754        assert_eq!(locals.next(), Some(&local.candidate));
4755        assert_eq!(locals.next(), None);
4756        let remotes = list.remote_candidates();
4757        assert_eq!(remotes.len(), 1);
4758        assert_eq!(remotes[0], remote.candidate);
4759    }
4760
4761    // simplified version of ConnCheckList handle_binding_request that doesn't
4762    // update any state like ConnCheckList or even do peer-reflexive candidate
4763    // things
4764    fn handle_binding_request(
4765        local_auth: &ShortTermAuth,
4766        local_credentials: &Credentials,
4767        msg: &Message,
4768        from: SocketAddr,
4769        error_response: Option<u16>,
4770        response_address: Option<SocketAddr>,
4771    ) -> Result<Vec<u8>, StunError> {
4772        let local_stun_credentials = local_auth.credentials().unwrap().0.clone();
4773
4774        if let Some(error_msg) = Message::check_attribute_types(
4775            msg,
4776            &[
4777                Username::TYPE,
4778                Fingerprint::TYPE,
4779                MessageIntegrity::TYPE,
4780                IceControlled::TYPE,
4781                IceControlling::TYPE,
4782                Priority::TYPE,
4783                UseCandidate::TYPE,
4784            ],
4785            &[
4786                Username::TYPE,
4787                Fingerprint::TYPE,
4788                MessageIntegrity::TYPE,
4789                Priority::TYPE,
4790            ],
4791            MessageWriteVec::new(),
4792        ) {
4793            // failure -> send error response
4794            return Ok(error_msg.finish());
4795        }
4796
4797        if msg
4798            .validate_integrity(&local_stun_credentials.clone().into())
4799            .is_err()
4800        {
4801            let code = ErrorCode::builder(ErrorCode::UNAUTHORIZED).build().unwrap();
4802            let mut response = Message::builder_error(msg, MessageWriteVec::new());
4803            response.add_attribute(&code).unwrap();
4804            return Ok(response.finish());
4805        }
4806
4807        let ice_controlling = msg.attribute::<IceControlling>();
4808        let ice_controlled = msg.attribute::<IceControlled>();
4809        let username = msg.attribute::<Username>();
4810        let valid_username = username
4811            .map(|username| validate_username(username, local_credentials))
4812            .unwrap_or(false);
4813
4814        let mut response = if ice_controlling.is_err() && ice_controlled.is_err() {
4815            warn!("missing ice controlled/controlling attribute");
4816            let mut response = Message::builder_error(msg, MessageWriteVec::new());
4817            let error = ErrorCode::builder(ErrorCode::BAD_REQUEST).build()?;
4818            response.add_attribute(&error)?;
4819            response
4820        } else if !valid_username {
4821            let mut response = Message::builder_error(msg, MessageWriteVec::new());
4822            let error = ErrorCode::builder(ErrorCode::UNAUTHORIZED).build()?;
4823            response.add_attribute(&error)?;
4824            response
4825        } else if let Some(error_code) = error_response {
4826            info!("responding with error {}", error_code);
4827            let mut response = Message::builder_error(msg, MessageWriteVec::new());
4828            let error = ErrorCode::builder(error_code).build()?;
4829            response.add_attribute(&error)?;
4830            response
4831        } else {
4832            let mut response = Message::builder_success(msg, MessageWriteVec::new());
4833            let xor_addr =
4834                XorMappedAddress::new(response_address.unwrap_or(from), msg.transaction_id());
4835            response.add_attribute(&xor_addr).unwrap();
4836            response
4837        };
4838        response.add_message_integrity(&local_stun_credentials.into(), IntegrityAlgorithm::Sha1)?;
4839        response.add_fingerprint()?;
4840        Ok(response.finish())
4841    }
4842
4843    fn reply_to_conncheck<T: AsRef<[u8]>>(
4844        agent: &mut StunAgent,
4845        auth: &ShortTermAuth,
4846        credentials: &Credentials,
4847        transmit: Transmit<T>,
4848        error_response: Option<u16>,
4849        response_address: Option<SocketAddr>,
4850        now: Instant,
4851    ) -> Option<Transmit<Box<[u8]>>> {
4852        let offset = match transmit.transport {
4853            TransportType::Udp => 0,
4854            TransportType::Tcp => 2,
4855        };
4856        trace!("data: {:x?}", transmit.data.as_ref());
4857        match Message::from_bytes(&transmit.data.as_ref()[offset..]) {
4858            Err(e) => error!("error parsing STUN message {e:?}"),
4859            Ok(msg) => {
4860                debug!("received {} -> {}: {}", transmit.from, transmit.to, msg);
4861                if msg.has_class(MessageClass::Request) && msg.has_method(BINDING) {
4862                    let transmit = agent
4863                        .send(
4864                            handle_binding_request(
4865                                auth,
4866                                credentials,
4867                                &msg,
4868                                transmit.from,
4869                                error_response,
4870                                response_address,
4871                            )
4872                            .unwrap(),
4873                            transmit.from,
4874                            now,
4875                        )
4876                        .unwrap();
4877                    let transport = transmit.transport;
4878                    return Some(transmit.reinterpret_data(|data| transmit_send(transport, data)));
4879                }
4880            }
4881        }
4882        None
4883    }
4884
4885    #[test]
4886    fn conncheck_list_transmit() {
4887        let _log = crate::tests::test_init_log();
4888        let mut state = FineControl::builder().build();
4889        let now = Instant::ZERO;
4890
4891        let CheckListSetPollRet::Event {
4892            checklist_id: _,
4893            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
4894        } = state.local.checklist_set.poll(now)
4895        else {
4896            unreachable!();
4897        };
4898
4899        let CheckListSetPollRet::WaitUntil(_) = state.local.checklist_set.poll(now) else {
4900            unreachable!();
4901        };
4902        let Some(CheckListSetTransmit {
4903            checklist_id: _,
4904            transmit,
4905        }) = state.local.checklist_set.poll_transmit(now)
4906        else {
4907            unreachable!()
4908        };
4909        assert_eq!(
4910            transmit.transport,
4911            state.local.peer.candidate.transport_type
4912        );
4913        assert_eq!(transmit.from, state.local.peer.candidate.base_address);
4914        assert_eq!(transmit.to, state.remote.candidate.base_address);
4915        state.local_list().dump_check_state();
4916    }
4917
4918    fn assert_list_contains_checks(list: &mut ConnCheckList, pairs: Vec<&CandidatePair>) {
4919        list.dump_check_state();
4920        trace!("pairs  {:?}", pairs);
4921
4922        for (pair, check) in pairs.into_iter().zip(list.pairs.iter()) {
4923            assert_eq!(&check.pair, pair);
4924        }
4925    }
4926
4927    #[test]
4928    fn checklist_generate_checks() {
4929        let _log = crate::tests::test_init_log();
4930        let mut set = ConnCheckListSet::builder(0, true).build();
4931        let list = set.new_list();
4932        let list = set.mut_list(list).unwrap();
4933        list.add_component(1);
4934        list.add_component(2);
4935        let local1_removed = Peer::builder()
4936            .priority(1)
4937            .local_addr("127.0.0.1:1".parse().unwrap())
4938            .build();
4939        let local1 = Peer::builder()
4940            .priority(100)
4941            .local_addr("127.0.0.1:100".parse().unwrap())
4942            .build();
4943        let local1_ignored2 = Peer::builder()
4944            .priority(99)
4945            .local_addr("127.0.0.1:100".parse().unwrap())
4946            .build();
4947        let remote1 = Peer::builder()
4948            .priority(2)
4949            .local_addr("127.0.0.1:2".parse().unwrap())
4950            .build();
4951        let local2 = Peer::builder()
4952            .component_id(2)
4953            .priority(4)
4954            .local_addr("127.0.0.2:3".parse().unwrap())
4955            .build();
4956        let remote2 = Peer::builder()
4957            .component_id(2)
4958            .priority(6)
4959            .local_addr("127.0.0.2:4".parse().unwrap())
4960            .build();
4961        let local3 = Peer::builder()
4962            .priority(10)
4963            .local_addr("127.0.0.3:5".parse().unwrap())
4964            .build();
4965        let remote3 = Peer::builder()
4966            .priority(15)
4967            .local_addr("127.0.0.3:6".parse().unwrap())
4968            .build();
4969
4970        assert!(list.add_local_candidate(local1_removed.candidate.clone()));
4971        assert!(list.add_local_candidate(local1.candidate.clone()));
4972        assert!(!list.add_local_candidate(local1_ignored2.candidate.clone()));
4973        list.add_remote_candidate(remote1.candidate.clone());
4974        assert!(list.add_local_candidate(local2.candidate.clone()));
4975        list.add_remote_candidate(remote2.candidate.clone());
4976        assert!(list.add_local_candidate(local3.candidate.clone()));
4977        list.add_remote_candidate(remote3.candidate.clone());
4978
4979        let pair1 = CandidatePair::new(local1.candidate.clone(), remote3.candidate.clone());
4980        let pair2 = CandidatePair::new(local3.candidate.clone(), remote3.candidate);
4981        let pair3 = CandidatePair::new(local2.candidate, remote2.candidate);
4982        let pair4 = CandidatePair::new(local1.candidate, remote1.candidate.clone());
4983        let pair5 = CandidatePair::new(local3.candidate, remote1.candidate);
4984        assert_list_contains_checks(list, vec![&pair1, &pair2, &pair3, &pair4, &pair5]);
4985    }
4986
4987    #[test]
4988    fn checklists_initial_thaw() {
4989        let _log = crate::tests::test_init_log();
4990        let mut set = ConnCheckListSet::builder(0, true).build();
4991        let list1_id = set.new_list();
4992        let list2_id = set.new_list();
4993        let now = Instant::ZERO;
4994
4995        let local1 = Peer::builder()
4996            .foundation("0")
4997            .priority(1)
4998            .local_addr("127.0.0.1:1".parse().unwrap())
4999            .build();
5000        let remote1 = Peer::builder()
5001            .foundation("0")
5002            .priority(2)
5003            .local_addr("127.0.0.1:2".parse().unwrap())
5004            .build();
5005        let local2 = Peer::builder()
5006            .foundation("0")
5007            .priority(3)
5008            .local_addr("127.0.0.2:3".parse().unwrap())
5009            .build();
5010        let remote2 = Peer::builder()
5011            .foundation("0")
5012            .priority(4)
5013            .local_addr("127.0.0.2:4".parse().unwrap())
5014            .build();
5015        let local3 = Peer::builder()
5016            .foundation("1")
5017            .priority(7)
5018            .local_addr("127.0.0.3:5".parse().unwrap())
5019            .build();
5020        let remote3 = Peer::builder()
5021            .foundation("1")
5022            .priority(10)
5023            .local_addr("127.0.0.3:6".parse().unwrap())
5024            .build();
5025
5026        // generated pairs
5027        let pair1 = CandidatePair::new(local1.candidate.clone(), remote1.candidate.clone());
5028        let pair2 = CandidatePair::new(local3.candidate.clone(), remote3.candidate.clone());
5029        let pair3 = CandidatePair::new(local3.candidate.clone(), remote2.candidate.clone());
5030        let pair4 = CandidatePair::new(local2.candidate.clone(), remote3.candidate.clone());
5031        let pair5 = CandidatePair::new(local2.candidate.clone(), remote2.candidate.clone());
5032
5033        let list1 = set.mut_list(list1_id).unwrap();
5034        list1.add_component(1);
5035        list1.add_local_candidate(local1.candidate.clone());
5036        list1.add_remote_candidate(remote1.candidate.clone());
5037
5038        assert_list_contains_checks(list1, vec![&pair1]);
5039
5040        let list2 = set.mut_list(list2_id).unwrap();
5041        list2.add_component(1);
5042        list2.add_local_candidate(local2.candidate.clone());
5043        list2.add_remote_candidate(remote2.candidate.clone());
5044        list2.add_local_candidate(local3.candidate.clone());
5045        list2.add_remote_candidate(remote3.candidate.clone());
5046
5047        assert_list_contains_checks(list2, vec![&pair2, &pair3, &pair4, &pair5]);
5048
5049        // thaw the first checklist with only a single pair will unfreeze that pair
5050        let CheckListSetPollRet::Event {
5051            checklist_id: _,
5052            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
5053        } = set.poll(now)
5054        else {
5055            unreachable!();
5056        };
5057
5058        set.poll_transmit(now).unwrap();
5059
5060        let list2 = set.mut_list(list2_id).unwrap();
5061        list2.dump_check_state();
5062        let check2 = list2.matching_check(&pair2, Nominate::DontCare).unwrap();
5063        assert_eq!(check2.pair, pair2);
5064        assert_eq!(check2.state(), CandidatePairState::InProgress);
5065        let check3 = list2.matching_check(&pair3, Nominate::DontCare).unwrap();
5066        assert_eq!(check3.pair, pair3);
5067        assert_eq!(check3.state(), CandidatePairState::Waiting);
5068        let check4 = list2.matching_check(&pair4, Nominate::DontCare).unwrap();
5069        assert_eq!(check4.pair, pair4);
5070        assert_eq!(check4.state(), CandidatePairState::Waiting);
5071        let check5 = list2.matching_check(&pair5, Nominate::DontCare).unwrap();
5072        assert_eq!(check5.pair, pair5);
5073        assert_eq!(check5.state(), CandidatePairState::Frozen);
5074
5075        let list1 = set.mut_list(list1_id).unwrap();
5076        list1.dump_check_state();
5077        let check1 = list1.matching_check(&pair1, Nominate::DontCare).unwrap();
5078        assert_eq!(check1.pair, pair1);
5079        assert_eq!(check1.state(), CandidatePairState::Waiting);
5080
5081        // thaw the second checklist with 2*2 pairs will unfreeze only the foundations not
5082        // unfrozen by the first checklist, which means unfreezing 3 pairs
5083        let now = wait_advance(&mut set, now);
5084        let CheckListSetPollRet::WaitUntil(_) = set.poll(now) else {
5085            unreachable!();
5086        };
5087        set.poll_transmit(now).unwrap();
5088
5089        let list1 = set.mut_list(list1_id).unwrap();
5090        list1.dump_check_state();
5091        let check1 = list1.matching_check(&pair1, Nominate::DontCare).unwrap();
5092        assert_eq!(check1.pair, pair1);
5093        assert_eq!(check1.state(), CandidatePairState::Waiting);
5094
5095        let list2 = set.mut_list(list2_id).unwrap();
5096        list2.dump_check_state();
5097        let check2 = list2.matching_check(&pair2, Nominate::DontCare).unwrap();
5098        assert_eq!(check2.pair, pair2);
5099        assert_eq!(check2.state(), CandidatePairState::InProgress);
5100        let check3 = list2.matching_check(&pair3, Nominate::DontCare).unwrap();
5101        assert_eq!(check3.pair, pair3);
5102        assert_eq!(check3.state(), CandidatePairState::InProgress);
5103        let check4 = list2.matching_check(&pair4, Nominate::DontCare).unwrap();
5104        assert_eq!(check4.pair, pair4);
5105        assert_eq!(check4.state(), CandidatePairState::Waiting);
5106        let check5 = list2.matching_check(&pair5, Nominate::DontCare).unwrap();
5107        assert_eq!(check5.pair, pair5);
5108        assert_eq!(check5.state(), CandidatePairState::Frozen);
5109    }
5110
5111    #[derive(Debug)]
5112    struct FineControlPeer {
5113        component_id: usize,
5114        peer: Peer,
5115        checklist_set: ConnCheckListSet,
5116        checklist_id: usize,
5117    }
5118
5119    #[derive(Debug)]
5120    struct FineControl {
5121        local: FineControlPeer,
5122        remote: Peer,
5123    }
5124
5125    struct FineControlBuilder {
5126        trickle_ice: bool,
5127        controlling: bool,
5128        local_peer_builder: PeerBuilder,
5129        remote_peer_builder: PeerBuilder,
5130    }
5131
5132    impl Default for FineControlBuilder {
5133        fn default() -> Self {
5134            let local_credentials = Credentials::new("luser".into(), "lpass".into());
5135            let remote_credentials = Credentials::new("ruser".into(), "rpass".into());
5136            Self {
5137                trickle_ice: false,
5138                controlling: true,
5139                local_peer_builder: Peer::builder()
5140                    .foundation("0")
5141                    .local_credentials(local_credentials.clone())
5142                    .remote_credentials(remote_credentials.clone())
5143                    .local_addr("127.0.0.1:1".parse().unwrap()),
5144                remote_peer_builder: Peer::builder()
5145                    .foundation("0")
5146                    .local_credentials(remote_credentials.clone())
5147                    .remote_credentials(local_credentials.clone())
5148                    .local_addr("127.0.0.1:2".parse().unwrap()),
5149            }
5150        }
5151    }
5152
5153    impl FineControlBuilder {
5154        fn controlling(mut self, controlling: bool) -> Self {
5155            self.controlling = controlling;
5156            self
5157        }
5158
5159        fn trickle_ice(mut self, trickle_ice: bool) -> Self {
5160            self.trickle_ice = trickle_ice;
5161            self
5162        }
5163
5164        fn local_candidate(mut self, candidate: Candidate) -> Self {
5165            self.local_peer_builder.candidate = Some(candidate);
5166            self
5167        }
5168
5169        fn build(self) -> FineControl {
5170            let mut local_set = ConnCheckListSet::builder(0, self.controlling)
5171                .trickle_ice(self.trickle_ice)
5172                .build();
5173            let local_list = local_set.new_list();
5174            let local_list = local_set.mut_list(local_list).unwrap();
5175            local_list.add_component(1);
5176            let checklist_id = local_list.checklist_id;
5177
5178            let local_peer = self.local_peer_builder.build();
5179            let remote_peer = self.remote_peer_builder.build();
5180
5181            local_list.set_local_credentials(local_peer.local_credentials.clone().unwrap());
5182            local_list.set_remote_credentials(local_peer.remote_credentials.clone().unwrap());
5183            if !self.trickle_ice {
5184                local_list.add_local_candidate(local_peer.candidate.clone());
5185                local_list.add_remote_candidate(remote_peer.candidate.clone());
5186            }
5187
5188            FineControl {
5189                local: FineControlPeer {
5190                    component_id: 1,
5191                    peer: local_peer,
5192                    checklist_set: local_set,
5193                    checklist_id,
5194                },
5195                remote: remote_peer,
5196            }
5197        }
5198    }
5199
5200    impl FineControl {
5201        fn builder() -> FineControlBuilder {
5202            FineControlBuilder::default()
5203        }
5204
5205        fn local_list(&mut self) -> &mut ConnCheckList {
5206            self.local
5207                .checklist_set
5208                .mut_list(self.local.checklist_id)
5209                .unwrap()
5210        }
5211
5212        fn set_remote_credentials(&mut self, credentials: Credentials) {
5213            self.local.peer.remote_credentials = Some(credentials.clone());
5214            self.remote.local_credentials = Some(credentials.clone());
5215            self.local_list().set_remote_credentials(credentials);
5216        }
5217
5218        fn check_nomination(&mut self, pair: &CandidatePair, now: Instant) {
5219            let nominate_check = self
5220                .local_list()
5221                .matching_check(pair, Nominate::True)
5222                .unwrap();
5223            assert_eq!(nominate_check.state(), CandidatePairState::Waiting);
5224            let pair = nominate_check.pair.clone();
5225            let check_id = nominate_check.conncheck_id;
5226            assert!(self.local_list().is_triggered(&pair));
5227
5228            // perform one tick which will perform the nomination check
5229            send_next_check_and_response(&self.local.peer, &self.remote)
5230                .perform(&mut self.local.checklist_set, now);
5231
5232            let nominate_check = self.local_list().check_by_id(check_id).unwrap();
5233            assert_eq!(nominate_check.state(), CandidatePairState::Succeeded);
5234
5235            // check list is done
5236            assert_eq!(self.local_list().state(), CheckListState::Completed);
5237
5238            let CheckListSetPollRet::Event {
5239                checklist_id: _,
5240                event: ConnCheckEvent::SelectedPair(_cid, _selected_pair),
5241            } = self.local.checklist_set.poll(now)
5242            else {
5243                unreachable!();
5244            };
5245            let CheckListSetPollRet::Event {
5246                checklist_id: _,
5247                event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connected),
5248            } = self.local.checklist_set.poll(now)
5249            else {
5250                unreachable!();
5251            };
5252
5253            // perform one final tick attempt which should end the processing
5254            assert!(matches!(
5255                self.local.checklist_set.poll(now),
5256                CheckListSetPollRet::Completed
5257            ));
5258        }
5259
5260        fn recv_data<T: AsRef<[u8]> + core::fmt::Debug>(&mut self, data: T, now: Instant) {
5261            let pair = CandidatePair::new(
5262                self.local.peer.candidate.clone(),
5263                self.remote.candidate.clone(),
5264            );
5265            self.recv_data_with_pair(&pair, data, now);
5266        }
5267
5268        fn recv_data_with_pair<T: AsRef<[u8]> + core::fmt::Debug>(
5269            &mut self,
5270            pair: &CandidatePair,
5271            data: T,
5272            now: Instant,
5273        ) {
5274            let checklist_id = self.local.checklist_id;
5275            let transmit = Transmit::new(
5276                data.as_ref(),
5277                TransportType::Udp,
5278                pair.remote.address,
5279                pair.local.address,
5280            );
5281            let reply = self.local.checklist_set.incoming_data(
5282                checklist_id,
5283                pair.local.component_id,
5284                transmit,
5285                now,
5286            );
5287            assert_eq!(reply.data.unwrap().as_ref(), data.as_ref());
5288        }
5289    }
5290
5291    struct NextCheckAndResponse {
5292        #[allow(unused)]
5293        local_cand: Candidate,
5294        remote_agent: StunAgent,
5295        remote_auth: ShortTermAuth,
5296        remote_credentials: Credentials,
5297        turn_server: Option<TurnServer>,
5298        error_response: Option<u16>,
5299        response_address: Option<SocketAddr>,
5300        unhandled_reply: bool,
5301    }
5302
5303    impl NextCheckAndResponse {
5304        fn error_response(mut self, error_response: u16) -> Self {
5305            self.error_response = Some(error_response);
5306            self
5307        }
5308
5309        fn response_address(mut self, address: SocketAddr) -> Self {
5310            self.response_address = Some(address);
5311            self
5312        }
5313
5314        fn turn_server(mut self, server: TurnServer) -> Self {
5315            self.turn_server = Some(server);
5316            self
5317        }
5318
5319        fn take_turn_server(&mut self) -> Option<TurnServer> {
5320            self.turn_server.take()
5321        }
5322
5323        fn unhandled_reply(mut self) -> Self {
5324            self.unhandled_reply = true;
5325            self
5326        }
5327
5328        fn send(&mut self, set: &mut ConnCheckListSet, now: Instant) -> Transmit<Box<[u8]>> {
5329            // perform one tick which will start a connectivity check with the peer
5330            match set.poll(now) {
5331                CheckListSetPollRet::WaitUntil(next) => assert_eq!(next, now),
5332                ret => {
5333                    error!("{ret:?}");
5334                    unreachable!()
5335                }
5336            }
5337            let Some(transmit) = set.poll_transmit(now) else {
5338                unreachable!()
5339            };
5340            let mut transmit = transmit.transmit;
5341            debug!("tick");
5342
5343            // send a response (success or some kind of error like role-conflict)
5344            if let Some(turn) = self.turn_server.as_mut() {
5345                transmit = turn
5346                    .recv(transmit, now)
5347                    .unwrap()
5348                    .build()
5349                    .reinterpret_data(|data| data.into_boxed_slice());
5350            }
5351            transmit
5352        }
5353
5354        fn response<T: AsRef<[u8]>>(
5355            &mut self,
5356            set: &mut ConnCheckListSet,
5357            transmit: Transmit<T>,
5358            now: Instant,
5359        ) {
5360            let mut reply = reply_to_conncheck(
5361                &mut self.remote_agent,
5362                &self.remote_auth,
5363                &self.remote_credentials,
5364                transmit,
5365                self.error_response,
5366                self.response_address,
5367                now,
5368            )
5369            .unwrap();
5370            info!("reply: {reply:?}");
5371
5372            if let Some(turn) = self.turn_server.as_mut() {
5373                reply = turn
5374                    .recv(reply, now)
5375                    .unwrap()
5376                    .build()
5377                    .reinterpret_data(|data| data.into_boxed_slice());
5378            }
5379
5380            let checklist_id = set
5381                .checklists
5382                .iter()
5383                .map(|checklist| checklist.checklist_id)
5384                .next()
5385                .unwrap();
5386            let reply = set.incoming_data(checklist_id, 1, reply, now);
5387            trace!("reply: {reply:?}");
5388            if !self.unhandled_reply {
5389                assert!(reply.handled);
5390            }
5391        }
5392
5393        fn perform(&mut self, set: &mut ConnCheckListSet, now: Instant) {
5394            let transmit = self.send(set, now);
5395            self.response(set, transmit, now)
5396        }
5397    }
5398
5399    fn send_next_check_and_response(local_peer: &Peer, remote_peer: &Peer) -> NextCheckAndResponse {
5400        let (remote_agent, remote_auth, _local_auth) =
5401            remote_peer.stun_agent_with_remote(local_peer.candidate.address);
5402        NextCheckAndResponse {
5403            local_cand: local_peer.candidate.clone(),
5404            remote_agent,
5405            remote_auth,
5406            remote_credentials: remote_peer.local_credentials.clone().unwrap(),
5407            turn_server: None,
5408            error_response: None,
5409            response_address: None,
5410            unhandled_reply: false,
5411        }
5412    }
5413
5414    fn send_next_check_and_response2(
5415        local_cand: Candidate,
5416        remote_peer: &Peer,
5417    ) -> NextCheckAndResponse {
5418        let (remote_agent, remote_auth, _local_auth) =
5419            remote_peer.stun_agent_with_remote(local_cand.address);
5420        NextCheckAndResponse {
5421            local_cand,
5422            remote_agent,
5423            remote_auth,
5424            remote_credentials: remote_peer.local_credentials.clone().unwrap(),
5425            turn_server: None,
5426            error_response: None,
5427            response_address: None,
5428            unhandled_reply: false,
5429        }
5430    }
5431
5432    #[test]
5433    fn very_fine_control1() {
5434        let _log = crate::tests::test_init_log();
5435        let mut state = FineControl::builder().build();
5436        let now = Instant::ZERO;
5437        assert_eq!(state.local.component_id, 1);
5438
5439        let pair = CandidatePair::new(
5440            state.local.peer.candidate.clone(),
5441            state.remote.candidate.clone(),
5442        );
5443        let check = state
5444            .local_list()
5445            .matching_check(&pair, Nominate::False)
5446            .unwrap();
5447        assert_eq!(check.state(), CandidatePairState::Frozen);
5448        let check_id = check.conncheck_id;
5449
5450        let CheckListSetPollRet::Event {
5451            checklist_id: _,
5452            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
5453        } = state.local.checklist_set.poll(now)
5454        else {
5455            unreachable!();
5456        };
5457
5458        // perform one tick which will start a connectivity check with the peer
5459        send_next_check_and_response(&state.local.peer, &state.remote)
5460            .perform(&mut state.local.checklist_set, now);
5461        let check = state.local_list().check_by_id(check_id).unwrap();
5462        assert_eq!(check.state(), CandidatePairState::Succeeded);
5463
5464        let now = wait_advance(&mut state.local.checklist_set, now);
5465
5466        state.check_nomination(&pair, now);
5467
5468        state.local.checklist_set.close(now);
5469
5470        let CheckListSetPollRet::RemoveSocket {
5471            checklist_id: _,
5472            component_id: 1,
5473            transport: TransportType::Udp,
5474            local_addr,
5475            remote_addr: _,
5476        } = state.local.checklist_set.poll(now)
5477        else {
5478            unreachable!();
5479        };
5480        assert_eq!(local_addr, pair.local.base_address);
5481        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
5482            unreachable!();
5483        };
5484    }
5485
5486    #[test]
5487    fn role_conflict_response() {
5488        let _log = crate::tests::test_init_log();
5489        // start off in the controlled mode, otherwise, the test needs to do the nomination
5490        // check
5491        let mut state = FineControl::builder().controlling(false).build();
5492        let now = Instant::ZERO;
5493
5494        let pair = CandidatePair::new(
5495            state.local.peer.candidate.clone(),
5496            state.remote.candidate.clone(),
5497        );
5498        let check = state
5499            .local_list()
5500            .matching_check(&pair, Nominate::False)
5501            .unwrap();
5502        assert_eq!(check.state(), CandidatePairState::Frozen);
5503        let check_id = check.conncheck_id;
5504
5505        let CheckListSetPollRet::Event {
5506            checklist_id: _,
5507            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
5508        } = state.local.checklist_set.poll(now)
5509        else {
5510            unreachable!();
5511        };
5512
5513        // perform one tick which will start a connectivity check with the peer
5514        send_next_check_and_response(&state.local.peer, &state.remote)
5515            .error_response(ErrorCode::ROLE_CONFLICT)
5516            .perform(&mut state.local.checklist_set, now);
5517        state.local_list().dump_check_state();
5518        let check = state.local_list().check_by_id(check_id).unwrap();
5519        assert_eq!(check.state(), CandidatePairState::Failed);
5520
5521        // should have resulted in the check being retriggered (always a new
5522        // check in our implementation)
5523        let triggered_check = state
5524            .local_list()
5525            .matching_check(&pair, Nominate::False)
5526            .unwrap();
5527        let check_id = triggered_check.conncheck_id;
5528        let pair = triggered_check.pair.clone();
5529        assert!(state.local_list().is_triggered(&pair));
5530
5531        // perform the next tick which will have a different ice controlling/ed attribute
5532        let now = wait_advance(&mut state.local.checklist_set, now);
5533        send_next_check_and_response(&state.local.peer, &state.remote)
5534            .perform(&mut state.local.checklist_set, now);
5535        let triggered_check = state.local_list().check_by_id(check_id).unwrap();
5536        assert_eq!(triggered_check.state(), CandidatePairState::Succeeded);
5537
5538        let now = wait_advance(&mut state.local.checklist_set, now);
5539        state.check_nomination(&pair, now);
5540
5541        state.local.checklist_set.close(now);
5542
5543        let CheckListSetPollRet::RemoveSocket {
5544            checklist_id: _,
5545            component_id: 1,
5546            transport: TransportType::Udp,
5547            local_addr,
5548            remote_addr: _,
5549        } = state.local.checklist_set.poll(now)
5550        else {
5551            unreachable!();
5552        };
5553        assert_eq!(local_addr, pair.local.base_address);
5554        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
5555            unreachable!();
5556        };
5557    }
5558
5559    #[test]
5560    fn bad_username_conncheck() {
5561        let _log = crate::tests::test_init_log();
5562        let mut state = FineControl::builder().build();
5563        let now = Instant::ZERO;
5564        let local_list = state
5565            .local
5566            .checklist_set
5567            .mut_list(state.local.checklist_id)
5568            .unwrap();
5569
5570        // set the wrong credentials and observe the failure
5571        let wrong_credentials =
5572            Credentials::new(String::from("wronguser"), String::from("wrongpass"));
5573        local_list.set_local_credentials(wrong_credentials);
5574
5575        let pair = CandidatePair::new(
5576            state.local.peer.candidate.clone(),
5577            state.remote.candidate.clone(),
5578        );
5579        let check = local_list.matching_check(&pair, Nominate::False).unwrap();
5580        let check_id = check.conncheck_id;
5581        assert_eq!(check.state(), CandidatePairState::Frozen);
5582
5583        let CheckListSetPollRet::Event {
5584            checklist_id: _,
5585            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
5586        } = state.local.checklist_set.poll(now)
5587        else {
5588            unreachable!();
5589        };
5590
5591        // perform one tick which will start a connectivity check with the peer
5592        send_next_check_and_response(&state.local.peer, &state.remote)
5593            .error_response(ErrorCode::UNAUTHORIZED)
5594            .perform(&mut state.local.checklist_set, now);
5595        let check = state.local_list().check_by_id(check_id).unwrap();
5596        assert_eq!(check.state(), CandidatePairState::Failed);
5597
5598        // TODO: properly failing the checklist on all checks failing
5599        // check should be failed
5600        //assert_eq!(state.local_list().state(), CheckListState::Failed);
5601
5602        //assert!(matches!(
5603        //    state.local.checklist_set.poll(now),
5604        //    CheckListSetPollRet::Completed
5605        //));
5606    }
5607
5608    #[test]
5609    fn conncheck_tcp_active() {
5610        let _log = crate::tests::test_init_log();
5611        let mut state = FineControl::builder();
5612        state.local_peer_builder = state
5613            .local_peer_builder
5614            .transport(TransportType::Tcp)
5615            .tcp_type(TcpType::Active);
5616        state.remote_peer_builder = state
5617            .remote_peer_builder
5618            .transport(TransportType::Tcp)
5619            .tcp_type(TcpType::Passive);
5620        let mut state = state.build();
5621        let pair = CandidatePair::new(
5622            state.local.peer.candidate.clone(),
5623            state.remote.candidate.clone(),
5624        );
5625        let local_agent =
5626            StunAgent::builder(TransportType::Tcp, state.local.peer.candidate.base_address)
5627                .remote_addr(state.remote.candidate.address)
5628                .build();
5629        let mut local_auth = ShortTermAuth::new();
5630        let mut remote_auth = ShortTermAuth::new();
5631        state
5632            .local
5633            .peer
5634            .configure_stun_auth(&mut local_auth, &mut remote_auth);
5635        let mut remote_agent =
5636            StunAgent::builder(TransportType::Tcp, state.remote.candidate.address)
5637                .remote_addr(state.local.peer.candidate.base_address)
5638                .build();
5639        let now = Instant::ZERO;
5640
5641        let CheckListSetPollRet::AllocateSocket {
5642            checklist_id: id,
5643            component_id: cid,
5644            transport,
5645            local_addr: from,
5646            remote_addr: to,
5647        } = state.local.checklist_set.poll(now)
5648        else {
5649            unreachable!();
5650        };
5651        assert_eq!(id, state.local.checklist_id);
5652        assert_eq!(cid, state.local.peer.candidate.component_id);
5653        assert_eq!(from, state.local.peer.candidate.base_address);
5654        assert_eq!(to, state.remote.candidate.address);
5655        assert_eq!(transport, TransportType::Tcp);
5656        error!("tcp connect");
5657
5658        let CheckListSetPollRet::Event {
5659            checklist_id: _,
5660            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
5661        } = state.local.checklist_set.poll(now)
5662        else {
5663            unreachable!();
5664        };
5665
5666        state.local.checklist_set.allocated_socket(
5667            id,
5668            cid,
5669            transport,
5670            from,
5671            to,
5672            Ok(local_agent.local_addr()),
5673            now,
5674        );
5675        error!("tcp connect replied");
5676
5677        let Some(transmit) = state.local.checklist_set.poll_transmit(now) else {
5678            unreachable!();
5679        };
5680        assert_eq!(transmit.checklist_id, state.local.checklist_id);
5681        assert_eq!(
5682            transmit.transmit.from,
5683            state.local.peer.candidate.base_address
5684        );
5685        assert_eq!(transmit.transmit.to, state.remote.candidate.address);
5686        error!("tcp transmit");
5687
5688        let Some(response) = reply_to_conncheck(
5689            &mut remote_agent,
5690            &remote_auth,
5691            state.local.peer.remote_credentials.as_ref().unwrap(),
5692            transmit.transmit,
5693            None,
5694            None,
5695            now,
5696        ) else {
5697            unreachable!();
5698        };
5699        error!("tcp reply");
5700
5701        let check = state
5702            .local_list()
5703            .matching_check(&pair, Nominate::DontCare)
5704            .unwrap();
5705        assert_eq!(check.state(), CandidatePairState::InProgress);
5706
5707        state
5708            .local
5709            .checklist_set
5710            .incoming_data(state.local.checklist_id, 1, response, now);
5711        error!("tcp replied");
5712
5713        let now = wait_advance(&mut state.local.checklist_set, now);
5714        let CheckListSetPollRet::WaitUntil(_) = state.local.checklist_set.poll(now) else {
5715            unreachable!();
5716        };
5717        let transmit = state.local.checklist_set.poll_transmit(now).unwrap();
5718
5719        let Some(response) = reply_to_conncheck(
5720            &mut remote_agent,
5721            &remote_auth,
5722            state.local.peer.remote_credentials.as_ref().unwrap(),
5723            transmit.transmit,
5724            None,
5725            None,
5726            now,
5727        ) else {
5728            unreachable!();
5729        };
5730        state
5731            .local
5732            .checklist_set
5733            .incoming_data(state.local.checklist_id, 1, response, now);
5734
5735        let CheckListSetPollRet::Event {
5736            checklist_id: _,
5737            event: ConnCheckEvent::SelectedPair(_cid, selected_pair),
5738        } = state.local.checklist_set.poll(now)
5739        else {
5740            unreachable!();
5741        };
5742        let CheckListSetPollRet::Event {
5743            checklist_id: _,
5744            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connected),
5745        } = state.local.checklist_set.poll(now)
5746        else {
5747            unreachable!();
5748        };
5749        assert_eq!(selected_pair.candidate_pair, pair);
5750
5751        assert!(matches!(
5752            state.local.checklist_set.poll(now),
5753            CheckListSetPollRet::Completed
5754        ));
5755
5756        state.local.checklist_set.close(now);
5757
5758        let CheckListSetPollRet::RemoveSocket {
5759            checklist_id: _,
5760            component_id: 1,
5761            transport: TransportType::Tcp,
5762            local_addr,
5763            remote_addr,
5764        } = state.local.checklist_set.poll(now)
5765        else {
5766            unreachable!();
5767        };
5768        assert_eq!(local_addr, pair.local.base_address);
5769        assert_eq!(remote_addr, pair.remote.address);
5770        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
5771            unreachable!();
5772        };
5773    }
5774
5775    #[test]
5776    fn conncheck_tcp_passive() {
5777        let _log = crate::tests::test_init_log();
5778        let mut state = FineControl::builder();
5779        state.local_peer_builder = state
5780            .local_peer_builder
5781            .transport(TransportType::Tcp)
5782            .local_addr("127.0.0.1:1000".parse().unwrap())
5783            .tcp_type(TcpType::Passive);
5784        state.remote_peer_builder = state
5785            .remote_peer_builder
5786            .transport(TransportType::Tcp)
5787            .local_addr("127.0.0.1:9".parse().unwrap())
5788            .tcp_type(TcpType::Active)
5789            .priority(10);
5790        let mut state = state.build();
5791        let now = Instant::ZERO;
5792        let remote_addr = SocketAddr::new(state.remote.candidate.base_address.ip(), 2000);
5793        let mut remote_cand = state.remote.candidate.clone();
5794        remote_cand.address = remote_addr;
5795        remote_cand.base_address = remote_addr;
5796
5797        let pair = CandidatePair::new(state.local.peer.candidate.clone(), remote_cand.clone());
5798        let mut local_auth = ShortTermAuth::new();
5799        let mut remote_auth = ShortTermAuth::new();
5800        state
5801            .local
5802            .peer
5803            .configure_stun_auth(&mut local_auth, &mut remote_auth);
5804        let mut remote_agent = StunAgent::builder(TransportType::Tcp, remote_addr)
5805            .remote_addr(state.local.peer.candidate.base_address)
5806            .build();
5807
5808        let request = ConnCheck::generate_stun_request(
5809            &pair,
5810            false,
5811            false,
5812            100,
5813            state.remote.local_credentials.clone().unwrap(),
5814            state.remote.remote_credentials.clone().unwrap(),
5815        )
5816        .unwrap();
5817
5818        let transport = remote_agent.transport();
5819        assert!(
5820            state
5821                .local
5822                .checklist_set
5823                .incoming_data(
5824                    state.local.checklist_id,
5825                    1,
5826                    remote_agent
5827                        .send_request(
5828                            request.finish(),
5829                            state.local.peer.candidate.base_address,
5830                            now
5831                        )
5832                        .unwrap()
5833                        .reinterpret_data(|data| transmit_send(transport, data)),
5834                    now,
5835                )
5836                .handled
5837        );
5838
5839        let check = state
5840            .local_list()
5841            .matching_check(&pair, Nominate::False)
5842            .unwrap();
5843        assert_eq!(check.state(), CandidatePairState::Waiting);
5844
5845        // success response
5846        let now = Instant::ZERO;
5847        let transmit = state.local.checklist_set.poll_transmit(now).unwrap();
5848        assert_eq!(transmit.checklist_id, state.local.checklist_id);
5849        assert_eq!(
5850            transmit.transmit.from,
5851            state.local.peer.candidate.base_address
5852        );
5853        assert_eq!(transmit.transmit.to, remote_cand.address);
5854
5855        let response = Message::from_bytes(&transmit.transmit.data[2..]).unwrap();
5856        assert!(response.has_class(MessageClass::Success));
5857
5858        let now = wait_advance(&mut state.local.checklist_set, now);
5859        let CheckListSetPollRet::Event {
5860            checklist_id: _,
5861            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
5862        } = state.local.checklist_set.poll(now)
5863        else {
5864            unreachable!();
5865        };
5866
5867        // triggered check
5868        let Some(transmit) = state.local.checklist_set.poll_transmit(now) else {
5869            unreachable!();
5870        };
5871        assert_eq!(transmit.checklist_id, state.local.checklist_id);
5872        assert_eq!(
5873            transmit.transmit.from,
5874            state.local.peer.candidate.base_address
5875        );
5876        assert_eq!(transmit.transmit.to, remote_cand.address);
5877
5878        let Some(response) = reply_to_conncheck(
5879            &mut remote_agent,
5880            &remote_auth,
5881            state.local.peer.remote_credentials.as_ref().unwrap(),
5882            transmit.transmit,
5883            None,
5884            None,
5885            now,
5886        ) else {
5887            unreachable!();
5888        };
5889        state.local_list().dump_check_state();
5890
5891        let check = state
5892            .local_list()
5893            .matching_check(&pair, Nominate::DontCare)
5894            .unwrap();
5895        assert_eq!(check.state(), CandidatePairState::InProgress);
5896
5897        state
5898            .local
5899            .checklist_set
5900            .incoming_data(state.local.checklist_id, 1, response, now);
5901        error!("tcp replied");
5902
5903        let now = wait_advance(&mut state.local.checklist_set, now);
5904        let CheckListSetPollRet::WaitUntil(_) = state.local.checklist_set.poll(now) else {
5905            unreachable!();
5906        };
5907        // nominate triggered check
5908        let transmit = state.local.checklist_set.poll_transmit(now).unwrap();
5909
5910        let Some(response) = reply_to_conncheck(
5911            &mut remote_agent,
5912            &remote_auth,
5913            state.local.peer.remote_credentials.as_ref().unwrap(),
5914            transmit.transmit,
5915            None,
5916            None,
5917            now,
5918        ) else {
5919            unreachable!();
5920        };
5921
5922        state
5923            .local
5924            .checklist_set
5925            .incoming_data(state.local.checklist_id, 1, response, now);
5926
5927        let CheckListSetPollRet::Event {
5928            checklist_id: _,
5929            event: ConnCheckEvent::SelectedPair(_cid, selected_pair),
5930        } = state.local.checklist_set.poll(now)
5931        else {
5932            unreachable!();
5933        };
5934        let CheckListSetPollRet::Event {
5935            checklist_id: _,
5936            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connected),
5937        } = state.local.checklist_set.poll(now)
5938        else {
5939            unreachable!();
5940        };
5941        assert!(candidate_pair_is_same_connection(
5942            &selected_pair.candidate_pair,
5943            &pair
5944        ));
5945
5946        assert!(matches!(
5947            state.local.checklist_set.poll(now),
5948            CheckListSetPollRet::Completed
5949        ));
5950
5951        state.local.checklist_set.close(now);
5952
5953        let CheckListSetPollRet::RemoveSocket {
5954            checklist_id: _,
5955            component_id: 1,
5956            transport: TransportType::Tcp,
5957            local_addr,
5958            remote_addr,
5959        } = state.local.checklist_set.poll(now)
5960        else {
5961            unreachable!();
5962        };
5963        assert_eq!(local_addr, pair.local.base_address);
5964        assert_eq!(remote_addr, pair.remote.address);
5965        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
5966            unreachable!();
5967        };
5968    }
5969
5970    fn remote_generate_check<'a>(
5971        remote_peer: &Peer,
5972        remote_agent: &'a mut StunAgent,
5973        to: SocketAddr,
5974        nominate: bool,
5975        controlling: bool,
5976        now: Instant,
5977    ) -> Transmit<Data<'a>> {
5978        // send a request from some unknown to the local agent address to produce a peer
5979        // reflexive candidate on the local agent
5980        let mut request = Message::builder_request(BINDING, MessageWriteVec::new());
5981        let priority = Priority::new(remote_peer.candidate.priority);
5982        request.add_attribute(&priority).unwrap();
5983        if controlling {
5984            request.add_attribute(&IceControlling::new(200)).unwrap();
5985        } else {
5986            request.add_attribute(&IceControlled::new(200)).unwrap();
5987        }
5988        let username = Username::new(
5989            &(remote_peer.remote_credentials.clone().unwrap().ufrag
5990                + ":"
5991                + &remote_peer.local_credentials.clone().unwrap().ufrag),
5992        )
5993        .unwrap();
5994        request.add_attribute(&username).unwrap();
5995        if nominate {
5996            request.add_attribute(&UseCandidate::new()).unwrap();
5997        }
5998        request
5999            .add_message_integrity(
6000                &MessageIntegrityCredentials::ShortTerm(
6001                    remote_peer.remote_credentials.clone().unwrap().into(),
6002                ),
6003                IntegrityAlgorithm::Sha1,
6004            )
6005            .unwrap();
6006        request.add_fingerprint().unwrap();
6007
6008        remote_agent
6009            .send_request(request.finish(), to, now)
6010            .unwrap()
6011    }
6012
6013    #[test]
6014    fn conncheck_incoming_prflx() {
6015        let _log = crate::tests::test_init_log();
6016        let mut state = FineControl::builder().build();
6017        let now = Instant::ZERO;
6018
6019        let pair = CandidatePair::new(
6020            state.local.peer.candidate.clone(),
6021            state.remote.candidate.clone(),
6022        );
6023        let initial_check = state
6024            .local_list()
6025            .matching_check(&pair, Nominate::False)
6026            .unwrap();
6027        assert_eq!(initial_check.state(), CandidatePairState::Frozen);
6028
6029        let unknown_remote_peer = Peer::builder()
6030            .local_addr("127.0.0.1:90".parse().unwrap())
6031            .foundation("1")
6032            .local_credentials(state.remote.local_credentials.clone().unwrap())
6033            .remote_credentials(state.local.peer.local_credentials.clone().unwrap())
6034            .build();
6035        let (mut remote_agent, _remote_auth, mut local_auth) = unknown_remote_peer.stun_agent();
6036
6037        let local_addr = state.local.peer.candidate.base_address;
6038        let transmit = remote_generate_check(
6039            &unknown_remote_peer,
6040            &mut remote_agent,
6041            local_addr,
6042            false,
6043            false,
6044            now,
6045        );
6046
6047        info!("sending prflx request");
6048        let reply =
6049            state
6050                .local
6051                .checklist_set
6052                .incoming_data(state.local.checklist_id, 1, transmit, now);
6053        assert!(reply.handled);
6054
6055        let Some(transmit) = state.local.checklist_set.poll_transmit(now) else {
6056            unreachable!();
6057        };
6058
6059        let response = Message::from_bytes(&transmit.transmit.data).unwrap();
6060        assert!(matches!(
6061            local_auth.validate_incoming_message(&response).unwrap(),
6062            Some(IntegrityAlgorithm::Sha1)
6063        ));
6064        assert!(remote_agent.handle_stun_message(&response, transmit.transmit.from));
6065        assert_eq!(transmit.transmit.from, local_addr);
6066        assert!(response.has_class(MessageClass::Success));
6067        info!("have prflx response");
6068
6069        // The stun request has created a new peer reflexive triggered check
6070        let mut prflx_remote_candidate = unknown_remote_peer.candidate.clone();
6071        prflx_remote_candidate.candidate_type = CandidateType::PeerReflexive;
6072        let pair = CandidatePair::new(
6073            state.local.peer.candidate.clone(),
6074            unknown_remote_peer.candidate.clone(),
6075        );
6076        state.local_list().dump_check_state();
6077        let triggered_check = state
6078            .local_list()
6079            .matching_check(&pair, Nominate::False)
6080            .unwrap();
6081        assert_eq!(triggered_check.state(), CandidatePairState::Waiting);
6082        let check_id = triggered_check.conncheck_id;
6083
6084        let now = wait_advance(&mut state.local.checklist_set, now);
6085        let CheckListSetPollRet::Event {
6086            checklist_id: _,
6087            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
6088        } = state.local.checklist_set.poll(now)
6089        else {
6090            unreachable!();
6091        };
6092
6093        // perform one tick which will start a connectivity check with the peer
6094        info!("perform triggered check");
6095        send_next_check_and_response(&state.local.peer, &unknown_remote_peer)
6096            .perform(&mut state.local.checklist_set, now);
6097
6098        info!("have reply to triggered check");
6099        let triggered_check = state.local_list().check_by_id(check_id).unwrap();
6100        assert_eq!(triggered_check.state(), CandidatePairState::Succeeded);
6101        let nominated_check = state
6102            .local_list()
6103            .matching_check(&pair, Nominate::True)
6104            .unwrap();
6105        assert_eq!(nominated_check.state(), CandidatePairState::Waiting);
6106        let check_id = nominated_check.conncheck_id;
6107        info!("perform nominated check");
6108        let now = wait_advance(&mut state.local.checklist_set, now);
6109        send_next_check_and_response(&state.local.peer, &unknown_remote_peer)
6110            .perform(&mut state.local.checklist_set, now);
6111
6112        info!("have reply to nominated check");
6113        let nominated_check = state.local_list().check_by_id(check_id).unwrap();
6114        assert_eq!(nominated_check.state(), CandidatePairState::Succeeded);
6115
6116        let CheckListSetPollRet::Event {
6117            checklist_id: _,
6118            event: ConnCheckEvent::SelectedPair(_cid, _selected_pair),
6119        } = state.local.checklist_set.poll(now)
6120        else {
6121            unreachable!();
6122        };
6123        let CheckListSetPollRet::Event {
6124            checklist_id: _,
6125            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connected),
6126        } = state.local.checklist_set.poll(now)
6127        else {
6128            unreachable!();
6129        };
6130
6131        assert!(matches!(
6132            state.local.checklist_set.poll(now),
6133            CheckListSetPollRet::Completed,
6134        ));
6135
6136        state.local.checklist_set.close(now);
6137
6138        let CheckListSetPollRet::RemoveSocket {
6139            checklist_id: _,
6140            component_id: 1,
6141            transport: TransportType::Udp,
6142            local_addr,
6143            remote_addr: _,
6144        } = state.local.checklist_set.poll(now)
6145        else {
6146            unreachable!();
6147        };
6148        assert_eq!(local_addr, pair.local.base_address);
6149        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
6150            unreachable!();
6151        };
6152    }
6153
6154    #[test]
6155    fn conncheck_response_prflx() {
6156        let _log = crate::tests::test_init_log();
6157        let mut state = FineControl::builder().build();
6158        let now = Instant::ZERO;
6159
6160        let pair = CandidatePair::new(
6161            state.local.peer.candidate.clone(),
6162            state.remote.candidate.clone(),
6163        );
6164        let initial_check = state
6165            .local_list()
6166            .matching_check(&pair, Nominate::False)
6167            .unwrap();
6168        assert_eq!(initial_check.state(), CandidatePairState::Frozen);
6169        let check_id = initial_check.conncheck_id;
6170
6171        let unknown_remote_peer = Peer::builder()
6172            .foundation("1")
6173            .local_credentials(state.remote.local_credentials.clone().unwrap())
6174            .remote_credentials(state.local.peer.local_credentials.clone().unwrap())
6175            .build();
6176        let (remote_agent, _remote_auth, _local_auth) = unknown_remote_peer.stun_agent();
6177
6178        let CheckListSetPollRet::Event {
6179            checklist_id: _,
6180            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
6181        } = state.local.checklist_set.poll(now)
6182        else {
6183            unreachable!();
6184        };
6185
6186        // send the next connectivity check but response with a different xor-mapped-address
6187        // which should result in a PeerReflexive address being produced in the check list
6188        send_next_check_and_response(&state.local.peer, &state.remote)
6189            .response_address(remote_agent.local_addr())
6190            .perform(&mut state.local.checklist_set, now);
6191        let initial_check = state.local_list().check_by_id(check_id).unwrap();
6192        assert_eq!(initial_check.state(), CandidatePairState::Succeeded);
6193
6194        // construct the peer reflexive pair
6195        let unknown_pair = CandidatePair::new(
6196            Candidate::builder(
6197                unknown_remote_peer.candidate.component_id,
6198                CandidateType::PeerReflexive,
6199                TransportType::Udp,
6200                "0",
6201                unknown_remote_peer.candidate.address,
6202            )
6203            .base_address(state.local.peer.candidate.base_address)
6204            .build(),
6205            state.remote.candidate.clone(),
6206        );
6207        let nominated_check = state
6208            .local_list()
6209            .matching_check(&unknown_pair, Nominate::True)
6210            .unwrap();
6211        assert_eq!(nominated_check.state(), CandidatePairState::Waiting);
6212        let check_id = nominated_check.conncheck_id;
6213
6214        state.local_list().dump_check_state();
6215
6216        let now = wait_advance(&mut state.local.checklist_set, now);
6217        send_next_check_and_response(&state.local.peer, &state.remote)
6218            .response_address(unknown_remote_peer.candidate.address)
6219            .perform(&mut state.local.checklist_set, now);
6220        let nominated_check = state.local_list().check_by_id(check_id).unwrap();
6221        assert_eq!(nominated_check.state(), CandidatePairState::Succeeded);
6222
6223        let CheckListSetPollRet::Event {
6224            checklist_id: _,
6225            event: ConnCheckEvent::SelectedPair(_cid, _selected_pair),
6226        } = state.local.checklist_set.poll(now)
6227        else {
6228            unreachable!();
6229        };
6230        let CheckListSetPollRet::Event {
6231            checklist_id: _,
6232            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connected),
6233        } = state.local.checklist_set.poll(now)
6234        else {
6235            unreachable!();
6236        };
6237
6238        assert!(matches!(
6239            state.local.checklist_set.poll(now),
6240            CheckListSetPollRet::Completed,
6241        ));
6242
6243        state.local.checklist_set.close(now);
6244
6245        let CheckListSetPollRet::RemoveSocket {
6246            checklist_id: _,
6247            component_id: 1,
6248            transport: TransportType::Udp,
6249            local_addr,
6250            remote_addr: _,
6251        } = state.local.checklist_set.poll(now)
6252        else {
6253            unreachable!();
6254        };
6255        assert_eq!(local_addr, pair.local.base_address);
6256        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
6257            unreachable!();
6258        };
6259    }
6260
6261    #[test]
6262    fn conncheck_trickle_ice() {
6263        let _log = crate::tests::test_init_log();
6264        let mut state = FineControl::builder().trickle_ice(true).build();
6265        let now = Instant::ZERO;
6266        assert_eq!(state.local.component_id, 1);
6267
6268        // Don't generate any initial checks as they should be done as candidates are added to
6269        // the checklist
6270        let set_ret = state.local.checklist_set.poll(now);
6271        // a checklist with no candidates has nothing to do
6272        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
6273
6274        let local_candidate = state.local.peer.candidate.clone();
6275        state.local_list().add_local_candidate(local_candidate);
6276
6277        let set_ret = state.local.checklist_set.poll(now);
6278        // a checklist with only a local candidates has nothing to do
6279        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
6280
6281        let remote_candidate = state.remote.candidate.clone();
6282        state.local_list().add_remote_candidate(remote_candidate);
6283
6284        // adding one local and one remote candidate that can be paired should have generated
6285        // the relevant waiting check. Not frozen because there is not other check with the
6286        // same foundation that already exists
6287        let pair = CandidatePair::new(
6288            state.local.peer.candidate.clone(),
6289            state.remote.candidate.clone(),
6290        );
6291        let check = state
6292            .local_list()
6293            .matching_check(&pair, Nominate::False)
6294            .unwrap();
6295        assert_eq!(check.state(), CandidatePairState::Waiting);
6296        let check_id = check.conncheck_id;
6297
6298        let CheckListSetPollRet::Event {
6299            checklist_id: _,
6300            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
6301        } = state.local.checklist_set.poll(now)
6302        else {
6303            unreachable!();
6304        };
6305
6306        // perform one tick which will start a connectivity check with the peer
6307        send_next_check_and_response(&state.local.peer, &state.remote)
6308            .perform(&mut state.local.checklist_set, now);
6309        let check = state.local_list().check_by_id(check_id).unwrap();
6310        assert_eq!(check.state(), CandidatePairState::Succeeded);
6311
6312        state.local_list().dump_check_state();
6313
6314        let now = wait_advance(&mut state.local.checklist_set, now);
6315        state.check_nomination(&pair, now);
6316
6317        state.local.checklist_set.close(now);
6318
6319        let CheckListSetPollRet::RemoveSocket {
6320            checklist_id: _,
6321            component_id: 1,
6322            transport: TransportType::Udp,
6323            local_addr,
6324            remote_addr: _,
6325        } = state.local.checklist_set.poll(now)
6326        else {
6327            unreachable!();
6328        };
6329        assert_eq!(local_addr, pair.local.base_address);
6330        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
6331            unreachable!();
6332        };
6333    }
6334
6335    #[test]
6336    fn conncheck_trickle_ice_no_remote_candidates_fail() {
6337        let _log = crate::tests::test_init_log();
6338        let mut state = FineControl::builder().trickle_ice(true).build();
6339        let local_candidate = state.local.peer.candidate.clone();
6340
6341        // Don't generate any initial checks as they should be done as candidates are added to
6342        // the checklist
6343        let now = Instant::ZERO;
6344
6345        let set_ret = state.local.checklist_set.poll(now);
6346        // a checklist with no candidates has nothing to do
6347        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
6348
6349        state.local_list().add_local_candidate(local_candidate);
6350        state.local_list().end_of_local_candidates();
6351
6352        let set_ret = state.local.checklist_set.poll(now);
6353        // a checklist with only a local candidates has nothing to do
6354        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
6355
6356        state.local_list().end_of_remote_candidates();
6357
6358        let CheckListSetPollRet::Event {
6359            checklist_id: _,
6360            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Failed),
6361        } = state.local.checklist_set.poll(now)
6362        else {
6363            unreachable!();
6364        };
6365
6366        let set_ret = state.local.checklist_set.poll(now);
6367        assert!(matches!(set_ret, CheckListSetPollRet::Completed));
6368        // a checklist with only a local candidates but no more possible candidates will error
6369        assert_eq!(state.local_list().state(), CheckListState::Failed);
6370
6371        state.local.checklist_set.close(now);
6372        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
6373            unreachable!();
6374        };
6375    }
6376
6377    #[test]
6378    fn conncheck_set_trickle_no_checks() {
6379        let _log = crate::tests::test_init_log();
6380        // ensure that a set of empty lists does not busyloop
6381        let mut set = ConnCheckListSet::builder(0, false)
6382            .trickle_ice(true)
6383            .build();
6384        let _list1_id = set.new_list();
6385        let _list2_id = set.new_list();
6386
6387        let now = Instant::ZERO;
6388        let CheckListSetPollRet::WaitUntil(_now) = set.poll(now) else {
6389            unreachable!();
6390        };
6391
6392        set.close(now);
6393
6394        let CheckListSetPollRet::Completed = set.poll(now) else {
6395            unreachable!();
6396        };
6397
6398        let CheckListSetPollRet::Closed = set.poll(now) else {
6399            unreachable!();
6400        };
6401    }
6402
6403    #[test]
6404    fn conncheck_incoming_request_while_local_in_progress() {
6405        let _log = crate::tests::test_init_log();
6406        let mut state = FineControl::builder().build();
6407
6408        let pair = CandidatePair::new(
6409            state.local.peer.candidate.clone(),
6410            state.remote.candidate.clone(),
6411        );
6412        let initial_check = state
6413            .local_list()
6414            .matching_check(&pair, Nominate::False)
6415            .unwrap();
6416        assert_eq!(initial_check.state(), CandidatePairState::Frozen);
6417        let check_id = initial_check.conncheck_id;
6418
6419        // Don't generate any initial checks as they should be done as candidates are added to
6420        // the checklist
6421        let now = Instant::ZERO;
6422
6423        let CheckListSetPollRet::Event {
6424            checklist_id: _,
6425            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
6426        } = state.local.checklist_set.poll(now)
6427        else {
6428            unreachable!();
6429        };
6430
6431        // send the conncheck (unanswered)
6432        let set_ret = state.local.checklist_set.poll(now);
6433        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
6434        let Some(_) = state.local.checklist_set.poll_transmit(now) else {
6435            unreachable!()
6436        };
6437        let initial_check = state.local_list().check_by_id(check_id).unwrap();
6438        assert_eq!(initial_check.state(), CandidatePairState::InProgress);
6439        let set_ret = state.local.checklist_set.poll(now);
6440        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
6441
6442        let (mut remote_agent, _remote_auth, _local_auth) = state.remote.stun_agent();
6443        let local_addr = state.local.peer.stun_agent().0.local_addr();
6444        let transmit = remote_generate_check(
6445            &state.remote,
6446            &mut remote_agent,
6447            local_addr,
6448            false,
6449            false,
6450            now,
6451        );
6452
6453        info!("sending request");
6454        let reply =
6455            state
6456                .local
6457                .checklist_set
6458                .incoming_data(state.local.checklist_id, 1, transmit, now);
6459        assert!(reply.handled);
6460        // eat the success response
6461        state.local.checklist_set.poll_transmit(now).unwrap();
6462        let now = wait_advance(&mut state.local.checklist_set, now);
6463
6464        // after the remote has sent the check, the previous check should still be in the same
6465        // state
6466        let initial_check = state.local_list().check_by_id(check_id).unwrap();
6467        assert_eq!(initial_check.state(), CandidatePairState::InProgress);
6468        // TODO: check for retransmit cancellation
6469
6470        // a new triggered check should have been created
6471        let triggered_check = state
6472            .local_list()
6473            .pairs
6474            .iter()
6475            .find(|check| {
6476                check.conncheck_id != check_id
6477                    && check.pair == pair
6478                    && Nominate::False == check.nominate
6479            })
6480            .unwrap();
6481        assert_ne!(check_id, triggered_check.conncheck_id);
6482        let check_id = triggered_check.conncheck_id;
6483        info!("perform triggered check");
6484        send_next_check_and_response(&state.local.peer, &state.remote)
6485            .perform(&mut state.local.checklist_set, now);
6486        let triggered_check = state.local_list().check_by_id(check_id).unwrap();
6487        assert_eq!(triggered_check.state(), CandidatePairState::Succeeded);
6488
6489        let now = wait_advance(&mut state.local.checklist_set, now);
6490        state.check_nomination(&pair, now);
6491
6492        state.local.checklist_set.close(now);
6493
6494        let CheckListSetPollRet::RemoveSocket {
6495            checklist_id: _,
6496            component_id: 1,
6497            transport: TransportType::Udp,
6498            local_addr,
6499            remote_addr: _,
6500        } = state.local.checklist_set.poll(now)
6501        else {
6502            unreachable!();
6503        };
6504        assert_eq!(local_addr, pair.local.base_address);
6505        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
6506            unreachable!();
6507        };
6508    }
6509
6510    #[test]
6511    fn conncheck_check_double_triggered() {
6512        let _log = crate::tests::test_init_log();
6513        let mut state = FineControl::builder().controlling(false).build();
6514
6515        let pair = CandidatePair::new(
6516            state.local.peer.candidate.clone(),
6517            state.remote.candidate.clone(),
6518        );
6519        let initial_check = state
6520            .local_list()
6521            .matching_check(&pair, Nominate::False)
6522            .unwrap();
6523        assert_eq!(initial_check.state(), CandidatePairState::Frozen);
6524        let check_id = initial_check.conncheck_id;
6525
6526        // Don't generate any initial checks as they should be done as candidates are added to
6527        // the checklist
6528        let now = Instant::ZERO;
6529
6530        let CheckListSetPollRet::Event {
6531            checklist_id: _,
6532            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
6533        } = state.local.checklist_set.poll(now)
6534        else {
6535            unreachable!();
6536        };
6537
6538        // send the conncheck, reply with ROLE_CONFLICT
6539        // perform one tick which will start a connectivity check with the peer
6540        send_next_check_and_response(&state.local.peer, &state.remote)
6541            .error_response(ErrorCode::ROLE_CONFLICT)
6542            .perform(&mut state.local.checklist_set, now);
6543        let initial_check = state.local_list().check_by_id(check_id).unwrap();
6544        assert_eq!(initial_check.state(), CandidatePairState::Failed);
6545
6546        // then hold onto the triggered transmit which removes the check from the triggered queue
6547        let triggered_check = state
6548            .local_list()
6549            .matching_check(&pair, Nominate::False)
6550            .unwrap();
6551        let check_id = triggered_check.conncheck_id;
6552        let pair = triggered_check.pair.clone();
6553        assert!(state.local_list().is_triggered(&pair));
6554        let now = wait_advance(&mut state.local.checklist_set, now);
6555        let set_ret = state.local.checklist_set.poll(now);
6556        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
6557
6558        // receive a normal request as if the remote is doing its own possibly triggered check.
6559        // The handling of this will add another triggered check entry.
6560        let (mut remote_agent, _remote_auth, _local_auth) = state.remote.stun_agent();
6561        let local_addr = state.local.peer.stun_agent().0.local_addr();
6562        let transmit = remote_generate_check(
6563            &state.remote,
6564            &mut remote_agent,
6565            local_addr,
6566            false,
6567            false,
6568            now,
6569        );
6570
6571        info!("sending request");
6572        let reply =
6573            state
6574                .local
6575                .checklist_set
6576                .incoming_data(state.local.checklist_id, 1, transmit, now);
6577        assert!(reply.handled);
6578        // eat the success response
6579        let Some(CheckListSetTransmit {
6580            checklist_id: _,
6581            transmit: _,
6582        }) = state.local.checklist_set.poll_transmit(now)
6583        else {
6584            unreachable!()
6585        };
6586        // ignore response
6587        state.local.checklist_set.poll_transmit(now).unwrap();
6588
6589        // after the remote has sent the check, the previous check should still be in the same
6590        // state
6591        let triggered_check = state.local_list().check_by_id(check_id).unwrap();
6592        assert_eq!(triggered_check.state(), CandidatePairState::InProgress);
6593
6594        // a new triggered check should not have been created
6595        let triggered_check = state
6596            .local_list()
6597            .matching_check(&pair, Nominate::False)
6598            .unwrap();
6599        assert_eq!(check_id, triggered_check.conncheck_id);
6600
6601        // handling of this second triggered check used to produce a large wait that was exposed to
6602        // outside and could cause a visible stall.  Now that path panics if a check is attempted
6603        // to be started twice.
6604        info!("perform triggered check 2");
6605        let now = wait_advance(&mut state.local.checklist_set, now);
6606        send_next_check_and_response(&state.local.peer, &state.remote)
6607            .perform(&mut state.local.checklist_set, now);
6608        // we still haven't replied to the original triggered check
6609        let triggered_check = state.local_list().check_by_id(check_id).unwrap();
6610        assert_eq!(triggered_check.state(), CandidatePairState::InProgress);
6611
6612        let now = wait_advance(&mut state.local.checklist_set, now);
6613        state.check_nomination(&pair, now);
6614
6615        state.local.checklist_set.close(now);
6616
6617        let CheckListSetPollRet::RemoveSocket {
6618            checklist_id: _,
6619            component_id: 1,
6620            transport: TransportType::Udp,
6621            local_addr,
6622            remote_addr: _,
6623        } = state.local.checklist_set.poll(now)
6624        else {
6625            unreachable!();
6626        };
6627        assert_eq!(local_addr, pair.local.base_address);
6628        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
6629            unreachable!();
6630        };
6631    }
6632
6633    #[test]
6634    fn conncheck_trickle_ice_prflx_check_before_remote_credentials() {
6635        let _log = crate::tests::test_init_log();
6636        let mut state = FineControl::builder()
6637            .controlling(true)
6638            .trickle_ice(true)
6639            .build();
6640
6641        let local_candidate = state.local.peer.candidate.clone();
6642        state.local_list().add_local_candidate(local_candidate);
6643
6644        let remote_credentials = generate_random_credentials();
6645        state.local.peer.remote_credentials = Some(remote_credentials.clone());
6646        state.remote.local_credentials = Some(remote_credentials.clone());
6647        let remote_peer = Peer::builder()
6648            .local_addr(state.remote.candidate.base_address)
6649            .foundation(&state.remote.candidate.foundation)
6650            .local_credentials(remote_credentials.clone())
6651            .remote_credentials(state.local.peer.local_credentials.clone().unwrap())
6652            .build();
6653        let (mut remote_agent, _remote_auth, _local_auth) = state.remote.stun_agent();
6654        let now = Instant::ZERO;
6655        let to = state.local.peer.candidate.base_address;
6656        let transmit =
6657            remote_generate_check(&remote_peer, &mut remote_agent, to, false, false, now);
6658
6659        info!("sending prflx request");
6660        let reply =
6661            state
6662                .local
6663                .checklist_set
6664                .incoming_data(state.local.checklist_id, 1, transmit, now);
6665        assert!(reply.handled);
6666
6667        let mut peer_reflexive_remote = state.remote.candidate.clone();
6668        peer_reflexive_remote.candidate_type = CandidateType::PeerReflexive;
6669        // XXX: implementation detail...
6670        peer_reflexive_remote.foundation = String::from("rflx");
6671        let pair = CandidatePair::new(state.local.peer.candidate.clone(), peer_reflexive_remote);
6672
6673        let prflx_check = state
6674            .local_list()
6675            .matching_check(&pair, Nominate::False)
6676            .unwrap();
6677        assert_eq!(prflx_check.state(), CandidatePairState::Waiting);
6678        let check_id = prflx_check.conncheck_id;
6679        let ret = state.local.checklist_set.poll_transmit(now);
6680        // response to prflx request
6681        let Some(CheckListSetTransmit {
6682            checklist_id: _,
6683            transmit,
6684        }) = ret
6685        else {
6686            error!("{ret:?}");
6687            unreachable!()
6688        };
6689        assert_eq!(transmit.from, state.local.peer.candidate.base_address);
6690        assert_eq!(transmit.to, state.remote.candidate.base_address);
6691        let response = Message::from_bytes(&transmit.data).unwrap();
6692        response
6693            .validate_integrity(&MessageIntegrityCredentials::ShortTerm(
6694                state.local.peer.local_credentials.clone().unwrap().into(),
6695            ))
6696            .unwrap();
6697
6698        let now = wait_advance(&mut state.local.checklist_set, now);
6699        let CheckListSetPollRet::Event {
6700            checklist_id: _,
6701            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
6702        } = state.local.checklist_set.poll(now)
6703        else {
6704            unreachable!();
6705        };
6706
6707        // send the triggered conncheck which will fail due to incorrect credentials and be ignored
6708        send_next_check_and_response(&state.local.peer, &state.remote)
6709            .unhandled_reply()
6710            .perform(&mut state.local.checklist_set, now);
6711        let prflx_check = state.local_list().check_by_id(check_id).unwrap();
6712        assert_eq!(prflx_check.state(), CandidatePairState::InProgress);
6713
6714        // correct remote credentials arrive
6715        info!("Correct remote credentials set");
6716        state.set_remote_credentials(remote_credentials);
6717
6718        // send the updated check which should succeed
6719        let now = wait_advance(&mut state.local.checklist_set, now);
6720        send_next_check_and_response(&state.local.peer, &state.remote)
6721            .perform(&mut state.local.checklist_set, now);
6722        let prflx_check = state
6723            .local_list()
6724            .matching_check(&pair, Nominate::False)
6725            .unwrap();
6726        assert_eq!(prflx_check.state(), CandidatePairState::Succeeded);
6727
6728        let now = wait_advance(&mut state.local.checklist_set, now);
6729        state.check_nomination(&pair, now);
6730
6731        state.local.checklist_set.close(now);
6732
6733        let CheckListSetPollRet::RemoveSocket {
6734            checklist_id: _,
6735            component_id: 1,
6736            transport: TransportType::Udp,
6737            local_addr,
6738            remote_addr: _,
6739        } = state.local.checklist_set.poll(now)
6740        else {
6741            unreachable!();
6742        };
6743        assert_eq!(local_addr, pair.local.base_address);
6744        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
6745            unreachable!();
6746        };
6747    }
6748
6749    fn turn_allocate(
6750        client: &mut TurnClient,
6751        server: &mut TurnServer,
6752        turn_alloc_addr: SocketAddr,
6753        now: Instant,
6754    ) -> Instant {
6755        let transmit = client.poll_transmit(now).unwrap();
6756        let msg = Message::from_bytes(&transmit.data).unwrap();
6757        assert!(msg.has_method(ALLOCATE));
6758        let transmit = server.recv(transmit, now).unwrap().build();
6759        let ret = client.recv(transmit, now);
6760        assert!(matches!(ret, TurnRecvRet::Handled));
6761        let TurnPollRet::WaitUntil(now) = client.poll(now) else {
6762            unreachable!();
6763        };
6764        let transmit = client.poll_transmit(now).unwrap();
6765        let msg = Message::from_bytes(&transmit.data).unwrap();
6766        assert!(msg.has_method(ALLOCATE));
6767        assert!(server.recv(transmit, now).is_none());
6768        let TurnServerPollRet::AllocateSocket {
6769            transport,
6770            listen_addr,
6771            client_addr,
6772            allocation_transport,
6773            family,
6774        } = server.poll(now)
6775        else {
6776            unreachable!();
6777        };
6778        server.allocated_socket(
6779            transport,
6780            listen_addr,
6781            client_addr,
6782            allocation_transport,
6783            family,
6784            Ok(turn_alloc_addr),
6785            now,
6786        );
6787        let transmit = server.poll_transmit(now).unwrap();
6788        let ret = client.recv(transmit, now);
6789        assert!(matches!(ret, TurnRecvRet::Handled));
6790        assert!(client.relayed_addresses().count() > 0);
6791        now
6792    }
6793
6794    fn set_handle_permission(
6795        set: &mut ConnCheckListSet,
6796        turn_server: &mut TurnServer,
6797        now: Instant,
6798    ) -> Instant {
6799        let Some(transmit) = set.poll_transmit(now) else {
6800            unreachable!()
6801        };
6802        let msg = Message::from_bytes(&transmit.transmit.data).unwrap();
6803        assert_eq!(msg.method(), CREATE_PERMISSION);
6804        let checklist_id = transmit.checklist_id;
6805        let transmit = turn_server.recv(transmit.transmit, now).unwrap().build();
6806        let msg = Message::from_bytes(&transmit.data).unwrap();
6807        assert_eq!(msg.method(), CREATE_PERMISSION);
6808
6809        let transmit = Transmit::new(
6810            Data::from(transmit.data.as_slice()),
6811            transmit.transport,
6812            transmit.from,
6813            transmit.to,
6814        );
6815        set.incoming_data(checklist_id, 1, transmit, now);
6816        match set.poll(now) {
6817            CheckListSetPollRet::WaitUntil(now) => now,
6818            ret => {
6819                error!("{ret:?}");
6820                unreachable!()
6821            }
6822        }
6823    }
6824
6825    fn turn_allocate_udp(client_transport: TransportType) {
6826        let local_addr = "127.0.0.1:1".parse::<SocketAddr>().unwrap();
6827        let turn_addr = "127.0.0.1:3478".parse::<SocketAddr>().unwrap();
6828        let turn_alloc_addr = "127.0.0.1:3000".parse::<SocketAddr>().unwrap();
6829        let turn_credentials = TurnCredentials::new("tuser", "tpass");
6830        let mut state = FineControl::builder()
6831            .local_candidate(
6832                Candidate::builder(
6833                    1,
6834                    CandidateType::Relayed,
6835                    TransportType::Udp,
6836                    "0",
6837                    turn_alloc_addr,
6838                )
6839                .priority(8000)
6840                .base_address(turn_alloc_addr)
6841                .related_address(local_addr)
6842                .build(),
6843            )
6844            .trickle_ice(true)
6845            .build();
6846
6847        let now = Instant::ZERO;
6848        let mut turn_server = TurnServer::new(client_transport, turn_addr, "realm".to_owned());
6849        turn_server.add_user(
6850            turn_credentials.username().to_owned(),
6851            turn_credentials.password().to_owned(),
6852        );
6853        let mut turn_client = match client_transport {
6854            TransportType::Udp => TurnClientUdp::allocate(
6855                local_addr,
6856                turn_addr,
6857                turn_client_proto::api::TurnConfig::new(turn_credentials),
6858            )
6859            .into(),
6860            TransportType::Tcp => TurnClientTcp::allocate(
6861                local_addr,
6862                turn_addr,
6863                turn_client_proto::api::TurnConfig::new(turn_credentials),
6864            )
6865            .into(),
6866        };
6867        let now = turn_allocate(&mut turn_client, &mut turn_server, turn_alloc_addr, now);
6868
6869        let remote_candidate = state.remote.candidate.clone();
6870        state.local_list().add_remote_candidate(remote_candidate);
6871        let local_candidate = state.local.peer.candidate.clone();
6872        state
6873            .local_list()
6874            .add_local_gathered_candidate(GatheredCandidate {
6875                candidate: local_candidate,
6876                turn_agent: Some(Box::new(turn_client)),
6877            });
6878        assert_eq!(state.local.component_id, 1);
6879
6880        let now = match state.local.checklist_set.poll(now) {
6881            CheckListSetPollRet::WaitUntil(now) => now,
6882            ret => {
6883                error!("{ret:?}");
6884                unreachable!()
6885            }
6886        };
6887        let now = set_handle_permission(&mut state.local.checklist_set, &mut turn_server, now);
6888
6889        let CheckListSetPollRet::Event {
6890            checklist_id: _,
6891            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
6892        } = state.local.checklist_set.poll(now)
6893        else {
6894            unreachable!();
6895        };
6896
6897        let pair = CandidatePair::new(
6898            state.local.peer.candidate.clone(),
6899            state.remote.candidate.clone(),
6900        );
6901        let check = state
6902            .local_list()
6903            .matching_check(&pair, Nominate::False)
6904            .unwrap();
6905        assert_eq!(check.state(), CandidatePairState::InProgress);
6906        let check_id = check.conncheck_id;
6907
6908        // perform one tick which will start a connectivity check with the peer
6909        let mut response =
6910            send_next_check_and_response(&state.local.peer, &state.remote).turn_server(turn_server);
6911        response.perform(&mut state.local.checklist_set, now);
6912        let turn_server = response.take_turn_server().unwrap();
6913        let check = state.local_list().check_by_id(check_id).unwrap();
6914        assert_eq!(check.state(), CandidatePairState::Succeeded);
6915
6916        // should have resulted in a nomination and therefore a triggered check (always a new
6917        // check in our implementation)
6918        let nominate_check = state
6919            .local_list()
6920            .matching_check(&pair, Nominate::True)
6921            .unwrap();
6922        let pair = nominate_check.pair.clone();
6923        let check_id = nominate_check.conncheck_id;
6924        assert!(state.local_list().is_triggered(&pair));
6925
6926        // perform one tick which will perform the nomination check
6927        let now = wait_advance(&mut state.local.checklist_set, now);
6928        let mut response =
6929            send_next_check_and_response(&state.local.peer, &state.remote).turn_server(turn_server);
6930        response.perform(&mut state.local.checklist_set, now);
6931        let mut turn_server = response.take_turn_server().unwrap();
6932
6933        error!("nominated id {check_id:?}");
6934        let nominate_check = state.local_list().check_by_id(check_id).unwrap();
6935        assert_eq!(nominate_check.state(), CandidatePairState::Succeeded);
6936
6937        // check list is done
6938        assert_eq!(state.local_list().state(), CheckListState::Completed);
6939
6940        let CheckListSetPollRet::Event {
6941            checklist_id: _,
6942            event: ConnCheckEvent::SelectedPair(_cid, _selected_pair),
6943        } = state.local.checklist_set.poll(now)
6944        else {
6945            unreachable!();
6946        };
6947        let CheckListSetPollRet::Event {
6948            checklist_id: _,
6949            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connected),
6950        } = state.local.checklist_set.poll(now)
6951        else {
6952            unreachable!();
6953        };
6954
6955        // perform one final tick attempt which should end the processing
6956        assert!(matches!(
6957            state.local.checklist_set.poll(now),
6958            CheckListSetPollRet::Completed
6959        ));
6960
6961        state.local.checklist_set.close(now);
6962
6963        // no RemoveSocket until the TURN DELETE is handled
6964        let CheckListSetPollRet::WaitUntil(now) = state.local.checklist_set.poll(now) else {
6965            unreachable!();
6966        };
6967
6968        let Some(transmit) = state.local.checklist_set.poll_transmit(now) else {
6969            unreachable!();
6970        };
6971
6972        let checklist_id = transmit.checklist_id;
6973        let transmit = turn_server.recv(transmit.transmit, now).unwrap().build();
6974        let transmit = Transmit::new(
6975            Data::from(transmit.data.as_slice()),
6976            transmit.transport,
6977            transmit.from,
6978            transmit.to,
6979        );
6980        let reply = state
6981            .local
6982            .checklist_set
6983            .incoming_data(checklist_id, 1, transmit, now);
6984        assert!(reply.handled);
6985
6986        let CheckListSetPollRet::RemoveSocket {
6987            checklist_id: _,
6988            component_id: 1,
6989            transport,
6990            local_addr: remove_local_addr,
6991            remote_addr: _,
6992        } = state.local.checklist_set.poll(now)
6993        else {
6994            unreachable!();
6995        };
6996        assert_eq!(transport, client_transport);
6997        assert_eq!(remove_local_addr, local_addr);
6998
6999        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
7000            unreachable!();
7001        };
7002    }
7003
7004    #[test]
7005    fn turn_udp_allocate_udp() {
7006        let _log = crate::tests::test_init_log();
7007        turn_allocate_udp(TransportType::Udp);
7008    }
7009
7010    #[test]
7011    fn turn_tcp_allocate_udp() {
7012        let _log = crate::tests::test_init_log();
7013        turn_allocate_udp(TransportType::Tcp);
7014    }
7015
7016    #[test]
7017    fn turn_udp_delayed_create_permission() {
7018        let _log = crate::tests::test_init_log();
7019        let local_addr = "127.0.0.1:1".parse::<SocketAddr>().unwrap();
7020        let turn_addr = "127.0.0.1:3478".parse::<SocketAddr>().unwrap();
7021        let turn_alloc_addr = "127.0.0.1:3000".parse::<SocketAddr>().unwrap();
7022        let turn_credentials = TurnCredentials::new("tuser", "tpass");
7023        let mut state = FineControl::builder()
7024            .local_candidate(
7025                Candidate::builder(
7026                    1,
7027                    CandidateType::Relayed,
7028                    TransportType::Udp,
7029                    "0",
7030                    turn_alloc_addr,
7031                )
7032                .priority(8000)
7033                .base_address(turn_alloc_addr)
7034                .related_address(local_addr)
7035                .build(),
7036            )
7037            .trickle_ice(true)
7038            .build();
7039
7040        let now = Instant::ZERO;
7041        let mut turn_server = TurnServer::new(TransportType::Udp, turn_addr, "realm".to_owned());
7042        turn_server.add_user(
7043            turn_credentials.username().to_owned(),
7044            turn_credentials.password().to_owned(),
7045        );
7046        let mut turn_client = TurnClientUdp::allocate(
7047            local_addr,
7048            turn_addr,
7049            turn_client_proto::api::TurnConfig::new(turn_credentials),
7050        )
7051        .into();
7052        let now = turn_allocate(&mut turn_client, &mut turn_server, turn_alloc_addr, now);
7053
7054        let remote_candidate = state.remote.candidate.clone();
7055        state.local_list().add_remote_candidate(remote_candidate);
7056        let local_candidate = state.local.peer.candidate.clone();
7057        state
7058            .local_list()
7059            .add_local_gathered_candidate(GatheredCandidate {
7060                candidate: local_candidate,
7061                turn_agent: Some(Box::new(turn_client)),
7062            });
7063        assert_eq!(state.local.component_id, 1);
7064
7065        let pair = CandidatePair::new(
7066            state.local.peer.candidate.clone(),
7067            state.remote.candidate.clone(),
7068        );
7069        let check = state
7070            .local_list()
7071            .matching_check(&pair, Nominate::False)
7072            .unwrap();
7073        assert_eq!(check.state(), CandidatePairState::Frozen);
7074        let check_id = check.conncheck_id;
7075
7076        let now = match state.local.checklist_set.poll(now) {
7077            CheckListSetPollRet::WaitUntil(now) => now,
7078            ret => {
7079                error!("{ret:?}");
7080                unreachable!()
7081            }
7082        };
7083        let Some(transmit) = state.local.checklist_set.poll_transmit(now) else {
7084            unreachable!()
7085        };
7086        let msg = Message::from_bytes(&transmit.transmit.data).unwrap();
7087        assert_eq!(msg.method(), CREATE_PERMISSION);
7088        let checklist_id = transmit.checklist_id;
7089        let transmit = turn_server.recv(transmit.transmit, now).unwrap().build();
7090        let msg = Message::from_bytes(&transmit.data).unwrap();
7091        assert_eq!(msg.method(), CREATE_PERMISSION);
7092
7093        let CheckListSetPollRet::WaitUntil(now) = state.local.checklist_set.poll(now) else {
7094            unreachable!();
7095        };
7096
7097        let transmit = Transmit::new(
7098            Data::from(transmit.data.as_slice()),
7099            transmit.transport,
7100            transmit.from,
7101            transmit.to,
7102        );
7103        state
7104            .local
7105            .checklist_set
7106            .incoming_data(checklist_id, 1, transmit, now);
7107
7108        let CheckListSetPollRet::Event {
7109            checklist_id: _,
7110            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
7111        } = state.local.checklist_set.poll(now)
7112        else {
7113            unreachable!();
7114        };
7115
7116        // perform one tick which will start a connectivity check with the peer
7117        let mut response =
7118            send_next_check_and_response(&state.local.peer, &state.remote).turn_server(turn_server);
7119        response.perform(&mut state.local.checklist_set, now);
7120        let turn_server = response.take_turn_server().unwrap();
7121        let check = state.local_list().check_by_id(check_id).unwrap();
7122        assert_eq!(check.state(), CandidatePairState::Succeeded);
7123
7124        // should have resulted in a nomination and therefore a triggered check (always a new
7125        // check in our implementation)
7126        let nominate_check = state
7127            .local_list()
7128            .matching_check(&pair, Nominate::True)
7129            .unwrap();
7130        let pair = nominate_check.pair.clone();
7131        let check_id = nominate_check.conncheck_id;
7132        assert!(state.local_list().is_triggered(&pair));
7133
7134        // perform one tick which will perform the nomination check
7135        let now = wait_advance(&mut state.local.checklist_set, now);
7136        let mut response =
7137            send_next_check_and_response(&state.local.peer, &state.remote).turn_server(turn_server);
7138        response.perform(&mut state.local.checklist_set, now);
7139
7140        error!("nominated id {check_id:?}");
7141        let nominate_check = state.local_list().check_by_id(check_id).unwrap();
7142        assert_eq!(nominate_check.state(), CandidatePairState::Succeeded);
7143
7144        // check list is done
7145        assert_eq!(state.local_list().state(), CheckListState::Completed);
7146
7147        let CheckListSetPollRet::Event {
7148            checklist_id: _,
7149            event: ConnCheckEvent::SelectedPair(_cid, _selected_pair),
7150        } = state.local.checklist_set.poll(now)
7151        else {
7152            unreachable!();
7153        };
7154        let CheckListSetPollRet::Event {
7155            checklist_id: _,
7156            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connected),
7157        } = state.local.checklist_set.poll(now)
7158        else {
7159            unreachable!();
7160        };
7161
7162        // perform one final tick attempt which should end the processing
7163        assert!(matches!(
7164            state.local.checklist_set.poll(now),
7165            CheckListSetPollRet::Completed
7166        ));
7167    }
7168
7169    fn set_handle_tcp_connect(
7170        set: &mut ConnCheckListSet,
7171        turn_server: &mut TurnServer,
7172        tcp_local_addr: SocketAddr,
7173        now: Instant,
7174    ) -> Instant {
7175        let transmit = set.poll_transmit(now).unwrap();
7176        let msg = Message::from_bytes(&transmit.transmit.data).unwrap();
7177        assert_eq!(msg.method(), CONNECT);
7178        let checklist_id = transmit.checklist_id;
7179        assert!(turn_server.recv(transmit.transmit, now).is_none());
7180
7181        let TurnServerPollRet::TcpConnect {
7182            relayed_addr,
7183            peer_addr,
7184            listen_addr,
7185            client_addr,
7186        } = turn_server.poll(now)
7187        else {
7188            unreachable!();
7189        };
7190        turn_server.tcp_connected(
7191            relayed_addr,
7192            peer_addr,
7193            listen_addr,
7194            client_addr,
7195            Ok(listen_addr),
7196            now,
7197        );
7198        let transmit = turn_server.poll_transmit(now).unwrap();
7199        let msg = Message::from_bytes(&transmit.data).unwrap();
7200        assert_eq!(msg.method(), CONNECT);
7201
7202        let transmit = Transmit::new(
7203            Data::from(transmit.data.as_slice()),
7204            transmit.transport,
7205            transmit.from,
7206            transmit.to,
7207        );
7208        let reply = set.incoming_data(checklist_id, 1, transmit, now);
7209        assert!(reply.handled);
7210        let CheckListSetPollRet::AllocateSocket {
7211            checklist_id,
7212            component_id,
7213            transport,
7214            local_addr,
7215            remote_addr,
7216        } = set.poll(now)
7217        else {
7218            unreachable!();
7219        };
7220        set.allocated_socket(
7221            checklist_id,
7222            component_id,
7223            transport,
7224            local_addr,
7225            remote_addr,
7226            Ok(tcp_local_addr),
7227            now,
7228        );
7229        let CheckListSetPollRet::WaitUntil(now) = set.poll(now) else {
7230            unreachable!();
7231        };
7232        let transmit = set.poll_transmit(now).unwrap();
7233        let msg = Message::from_bytes(&transmit.transmit.data).unwrap();
7234        assert!(msg.has_method(CONNECTION_BIND));
7235        let transmit = turn_server.recv(transmit.transmit, now).unwrap().build();
7236        let reply = set.incoming_data(checklist_id, 1, transmit, now);
7237        assert!(reply.handled);
7238        match set.poll(now) {
7239            CheckListSetPollRet::WaitUntil(now) => now,
7240            ret => {
7241                error!("{ret:?}");
7242                unreachable!()
7243            }
7244        }
7245    }
7246
7247    #[test]
7248    fn turn_tcp_create_permission() {
7249        let _log = crate::tests::test_init_log();
7250        let local_addr = "127.0.0.1:1".parse::<SocketAddr>().unwrap();
7251        let turn_addr = "127.0.0.1:3478".parse::<SocketAddr>().unwrap();
7252        let turn_alloc_addr = "127.0.0.1:3000".parse::<SocketAddr>().unwrap();
7253        let local_tcp_addr = "127.0.0.1:999".parse::<SocketAddr>().unwrap();
7254        let turn_credentials = TurnCredentials::new("tuser", "tpass");
7255        let mut builder = FineControl::builder()
7256            .local_candidate(
7257                Candidate::builder(
7258                    1,
7259                    CandidateType::Relayed,
7260                    TransportType::Tcp,
7261                    "0",
7262                    turn_alloc_addr,
7263                )
7264                .priority(8000)
7265                .base_address(turn_alloc_addr)
7266                .related_address(local_addr)
7267                .tcp_type(TcpType::Active)
7268                .build(),
7269            )
7270            .trickle_ice(true);
7271        builder.remote_peer_builder = builder
7272            .remote_peer_builder
7273            .transport(TransportType::Tcp)
7274            .tcp_type(TcpType::Passive);
7275        let mut state = builder.build();
7276
7277        let mut local_auth = ShortTermAuth::new();
7278        let mut remote_auth = ShortTermAuth::new();
7279        state
7280            .remote
7281            .configure_stun_auth(&mut remote_auth, &mut local_auth);
7282
7283        let now = Instant::ZERO;
7284        let mut turn_server = TurnServer::new(TransportType::Tcp, turn_addr, "realm".to_owned());
7285        turn_server.add_user(
7286            turn_credentials.username().to_owned(),
7287            turn_credentials.password().to_owned(),
7288        );
7289        let mut config = turn_client_proto::api::TurnConfig::new(turn_credentials);
7290        config.set_allocation_transport(TransportType::Tcp);
7291        let mut turn_client =
7292            TurnClient::from(TurnClientTcp::allocate(local_addr, turn_addr, config));
7293        let now = turn_allocate(&mut turn_client, &mut turn_server, turn_alloc_addr, now);
7294
7295        let remote_candidate = state.remote.candidate.clone();
7296        state.local_list().add_remote_candidate(remote_candidate);
7297        let local_candidate = state.local.peer.candidate.clone();
7298        state
7299            .local_list()
7300            .add_local_gathered_candidate(GatheredCandidate {
7301                candidate: local_candidate,
7302                turn_agent: Some(Box::new(turn_client)),
7303            });
7304        assert_eq!(state.local.component_id, 1);
7305
7306        let now = match state.local.checklist_set.poll(now) {
7307            CheckListSetPollRet::WaitUntil(now) => now,
7308            ret => {
7309                error!("{ret:?}");
7310                unreachable!()
7311            }
7312        };
7313        let now = set_handle_permission(&mut state.local.checklist_set, &mut turn_server, now);
7314        error!("{state:?}");
7315
7316        let CheckListSetPollRet::Event {
7317            checklist_id: _,
7318            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
7319        } = state.local.checklist_set.poll(now)
7320        else {
7321            unreachable!();
7322        };
7323
7324        let now = set_handle_tcp_connect(
7325            &mut state.local.checklist_set,
7326            &mut turn_server,
7327            local_tcp_addr,
7328            now,
7329        );
7330
7331        let pair = CandidatePair::new(
7332            state.local.peer.candidate.clone(),
7333            state.remote.candidate.clone(),
7334        );
7335        let check = state
7336            .local_list()
7337            .matching_check(&pair, Nominate::False)
7338            .unwrap();
7339        assert_eq!(check.state(), CandidatePairState::InProgress);
7340        let check_id = check.conncheck_id;
7341
7342        // perform one tick which will start a connectivity check with the peer
7343        let mut response =
7344            send_next_check_and_response(&state.local.peer, &state.remote).turn_server(turn_server);
7345        response.perform(&mut state.local.checklist_set, now);
7346        let turn_server = response.take_turn_server().unwrap();
7347        let check = state.local_list().check_by_id(check_id).unwrap();
7348        assert_eq!(check.state(), CandidatePairState::Succeeded);
7349
7350        // should have resulted in a nomination and therefore a triggered check (always a new
7351        // check in our implementation)
7352        let nominate_check = state
7353            .local_list()
7354            .matching_check(&pair, Nominate::True)
7355            .unwrap();
7356        let pair = nominate_check.pair.clone();
7357        let check_id = nominate_check.conncheck_id;
7358        assert!(state.local_list().is_triggered(&pair));
7359
7360        // perform one tick which will perform the nomination check
7361        let now = wait_advance(&mut state.local.checklist_set, now);
7362        let mut response =
7363            send_next_check_and_response(&state.local.peer, &state.remote).turn_server(turn_server);
7364        response.perform(&mut state.local.checklist_set, now);
7365        let mut turn_server = response.take_turn_server().unwrap();
7366
7367        error!("nominated id {check_id:?}");
7368        let nominate_check = state.local_list().check_by_id(check_id).unwrap();
7369        assert_eq!(nominate_check.state(), CandidatePairState::Succeeded);
7370
7371        // check list is done
7372        assert_eq!(state.local_list().state(), CheckListState::Completed);
7373
7374        let CheckListSetPollRet::Event {
7375            checklist_id: _,
7376            event: ConnCheckEvent::SelectedPair(_cid, _selected_pair),
7377        } = state.local.checklist_set.poll(now)
7378        else {
7379            unreachable!();
7380        };
7381        let CheckListSetPollRet::Event {
7382            checklist_id: _,
7383            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connected),
7384        } = state.local.checklist_set.poll(now)
7385        else {
7386            unreachable!();
7387        };
7388
7389        // perform one final tick attempt which should end the processing
7390        assert!(matches!(
7391            state.local.checklist_set.poll(now),
7392            CheckListSetPollRet::Completed
7393        ));
7394
7395        state.local.checklist_set.close(now);
7396
7397        // no RemoveSocket until the TURN DELETE is handled
7398        let CheckListSetPollRet::WaitUntil(now) = state.local.checklist_set.poll(now) else {
7399            unreachable!();
7400        };
7401
7402        let Some(transmit) = state.local.checklist_set.poll_transmit(now) else {
7403            unreachable!();
7404        };
7405
7406        let checklist_id = transmit.checklist_id;
7407        let transmit = turn_server.recv(transmit.transmit, now).unwrap().build();
7408        let transmit = Transmit::new(
7409            Data::from(transmit.data.as_slice()),
7410            transmit.transport,
7411            transmit.from,
7412            transmit.to,
7413        );
7414        let reply = state
7415            .local
7416            .checklist_set
7417            .incoming_data(checklist_id, 1, transmit, now);
7418        assert!(reply.handled);
7419
7420        let CheckListSetPollRet::RemoveSocket {
7421            checklist_id: _,
7422            component_id: 1,
7423            transport,
7424            local_addr: remove_local_addr,
7425            remote_addr: _,
7426        } = state.local.checklist_set.poll(now)
7427        else {
7428            unreachable!();
7429        };
7430        assert_eq!(transport, turn_server.transport());
7431        assert_eq!(remove_local_addr, local_addr);
7432
7433        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
7434            unreachable!();
7435        };
7436    }
7437
7438    fn wait_advance(set: &mut ConnCheckListSet, now: Instant) -> Instant {
7439        let CheckListSetPollRet::WaitUntil(next) = set.poll(now) else {
7440            unreachable!();
7441        };
7442        assert!(next > now);
7443        next
7444    }
7445
7446    #[test]
7447    fn timing_advance() {
7448        let _log = crate::tests::test_init_log();
7449        for ta in [Duration::from_millis(10), Duration::from_secs(1)] {
7450            let mut state = FineControl::builder().build();
7451            state.local.checklist_set.set_timing_advance(ta);
7452            let mut now = Instant::ZERO;
7453            assert_eq!(state.local.component_id, 1);
7454
7455            let local2 = Peer::builder()
7456                .foundation("1")
7457                .component_id(1)
7458                .priority(state.local.peer.candidate.priority - 1)
7459                .local_addr("127.0.0.2:3".parse().unwrap())
7460                .build();
7461            state
7462                .local_list()
7463                .add_local_candidate(local2.candidate.clone());
7464
7465            let pair1 = CandidatePair::new(
7466                state.local.peer.candidate.clone(),
7467                state.remote.candidate.clone(),
7468            );
7469            let check = state
7470                .local_list()
7471                .matching_check(&pair1, Nominate::False)
7472                .unwrap();
7473            assert_eq!(check.state(), CandidatePairState::Frozen);
7474
7475            let pair2 =
7476                CandidatePair::new(local2.candidate.clone(), state.remote.candidate.clone());
7477            let check = state
7478                .local_list()
7479                .matching_check(&pair2, Nominate::False)
7480                .unwrap();
7481            assert_eq!(check.state(), CandidatePairState::Frozen);
7482
7483            let CheckListSetPollRet::Event {
7484                checklist_id: _,
7485                event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
7486            } = state.local.checklist_set.poll(now)
7487            else {
7488                unreachable!();
7489            };
7490            let mut pending_checks = vec![];
7491            for pair in [&pair1, &pair2] {
7492                let check = state
7493                    .local_list()
7494                    .matching_check(pair, Nominate::False)
7495                    .unwrap();
7496                assert!(
7497                    [CandidatePairState::Waiting, CandidatePairState::InProgress]
7498                        .contains(&check.state())
7499                );
7500                let check_id = check.conncheck_id;
7501
7502                // perform one tick which will start a connectivity check with the peer
7503                let mut response = send_next_check_and_response2(pair.local.clone(), &state.remote);
7504                let transmit = response.send(&mut state.local.checklist_set, now);
7505
7506                let check = state.local_list().check_by_id(check_id).unwrap();
7507                assert_eq!(check.state(), CandidatePairState::InProgress);
7508                pending_checks.push((response, transmit));
7509                let new_now = wait_advance(&mut state.local.checklist_set, now);
7510                assert_eq!(new_now - now, ta);
7511                now = new_now;
7512            }
7513            for (mut response, transmit) in pending_checks {
7514                response.response(&mut state.local.checklist_set, transmit, now);
7515            }
7516
7517            state.check_nomination(&pair1, now);
7518
7519            state.local.checklist_set.close(now);
7520
7521            let CheckListSetPollRet::RemoveSocket {
7522                checklist_id: _,
7523                component_id: 1,
7524                transport: TransportType::Udp,
7525                local_addr,
7526                remote_addr: _,
7527            } = state.local.checklist_set.poll(now)
7528            else {
7529                unreachable!();
7530            };
7531            assert_eq!(local_addr, pair1.local.base_address);
7532            let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
7533                unreachable!();
7534            };
7535        }
7536    }
7537
7538    #[test]
7539    fn conncheck_request_retransmit_timings() {
7540        let _log = crate::tests::test_init_log();
7541        let mut state = FineControl::builder().build();
7542        state
7543            .local
7544            .checklist_set
7545            .set_request_retransmits(RequestRto::from_parts(
7546                Duration::from_secs(1),
7547                Duration::from_secs(2),
7548                4,
7549                Duration::from_secs(4),
7550            ));
7551        state.local_list().end_of_local_candidates();
7552        state.local_list().end_of_remote_candidates();
7553
7554        let pair = CandidatePair::new(
7555            state.local.peer.candidate.clone(),
7556            state.remote.candidate.clone(),
7557        );
7558        let initial_check = state
7559            .local_list()
7560            .matching_check(&pair, Nominate::False)
7561            .unwrap();
7562        assert_eq!(initial_check.state(), CandidatePairState::Frozen);
7563        let check_id = initial_check.conncheck_id;
7564
7565        // Don't generate any initial checks as they should be done as candidates are added to
7566        // the checklist
7567        let mut now = Instant::ZERO;
7568
7569        let CheckListSetPollRet::Event {
7570            checklist_id: _,
7571            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
7572        } = state.local.checklist_set.poll(now)
7573        else {
7574            unreachable!();
7575        };
7576
7577        // send the conncheck (unanswered)
7578        let set_ret = state.local.checklist_set.poll(now);
7579        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
7580        state.local.checklist_set.poll_transmit(now).unwrap();
7581        let new_now = wait_advance(&mut state.local.checklist_set, now);
7582        let new_now = wait_advance(&mut state.local.checklist_set, new_now);
7583        assert_eq!(new_now - now, Duration::from_secs(1));
7584        now = new_now;
7585
7586        let initial_check = state.local_list().check_by_id(check_id).unwrap();
7587        assert_eq!(initial_check.state(), CandidatePairState::InProgress);
7588
7589        let set_ret = state.local.checklist_set.poll(now);
7590        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
7591        state.local.checklist_set.poll_transmit(now).unwrap();
7592        let new_now = wait_advance(&mut state.local.checklist_set, now);
7593        let new_now = wait_advance(&mut state.local.checklist_set, new_now);
7594        assert_eq!(new_now - now, Duration::from_secs(2));
7595        now = new_now;
7596
7597        let set_ret = state.local.checklist_set.poll(now);
7598        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
7599        state.local.checklist_set.poll_transmit(now).unwrap();
7600        let new_now = wait_advance(&mut state.local.checklist_set, now);
7601        let new_now = wait_advance(&mut state.local.checklist_set, new_now);
7602        assert_eq!(new_now - now, Duration::from_secs(2));
7603        now = new_now;
7604
7605        let set_ret = state.local.checklist_set.poll(now);
7606        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
7607        state.local.checklist_set.poll_transmit(now).unwrap();
7608        let new_now = wait_advance(&mut state.local.checklist_set, now);
7609        let new_now = wait_advance(&mut state.local.checklist_set, new_now);
7610        assert_eq!(new_now - now, Duration::from_secs(4));
7611        now = new_now;
7612
7613        let CheckListSetPollRet::Completed = state.local.checklist_set.poll(now) else {
7614            unreachable!();
7615        };
7616
7617        let initial_check = state.local_list().check_by_id(check_id).unwrap();
7618        assert_eq!(initial_check.state(), CandidatePairState::Failed);
7619
7620        let CheckListSetPollRet::Event {
7621            checklist_id: _,
7622            event: ConnCheckEvent::ComponentState(1, ComponentConnectionState::Failed),
7623        } = state.local.checklist_set.poll(now)
7624        else {
7625            unreachable!();
7626        };
7627
7628        state.local.checklist_set.close(now);
7629
7630        let CheckListSetPollRet::RemoveSocket {
7631            checklist_id: _,
7632            component_id: 1,
7633            transport: TransportType::Udp,
7634            local_addr,
7635            remote_addr: _,
7636        } = state.local.checklist_set.poll(now)
7637        else {
7638            unreachable!();
7639        };
7640        assert_eq!(local_addr, pair.local.base_address);
7641        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
7642            unreachable!();
7643        };
7644    }
7645
7646    #[test]
7647    fn conncheck_trickle_ice_controlled_renomination() {
7648        let _log = crate::tests::test_init_log();
7649        let mut state = FineControl::builder()
7650            .trickle_ice(true)
7651            .controlling(false)
7652            .build();
7653        let now = Instant::ZERO;
7654        assert_eq!(state.local.component_id, 1);
7655
7656        // Don't generate any initial checks as they should be done as candidates are added to
7657        // the checklist
7658        let set_ret = state.local.checklist_set.poll(now);
7659        // a checklist with no candidates has nothing to do
7660        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
7661
7662        let local_candidate = state.local.peer.candidate.clone();
7663        let local_ipv6_addr = "[f8e0::1]:8".parse().unwrap();
7664        let mut local_candidate2 = state.local.peer.candidate.clone();
7665        local_candidate2.address = local_ipv6_addr;
7666        local_candidate2.base_address = local_ipv6_addr;
7667        local_candidate2.priority -= 1;
7668        local_candidate2.foundation = "ipv6".to_owned();
7669        state
7670            .local_list()
7671            .add_local_candidate(local_candidate.clone());
7672        state
7673            .local_list()
7674            .add_local_candidate(local_candidate2.clone());
7675
7676        let set_ret = state.local.checklist_set.poll(now);
7677        // a checklist with only a local candidates has nothing to do
7678        assert!(matches!(set_ret, CheckListSetPollRet::WaitUntil(_)));
7679
7680        let remote_candidate = state.remote.candidate.clone();
7681        let mut remote_candidate2 = state.remote.candidate.clone();
7682        let remote_ipv6_addr = "[f8e0::2]:9".parse().unwrap();
7683        remote_candidate2.address = remote_ipv6_addr;
7684        remote_candidate2.base_address = remote_ipv6_addr;
7685        remote_candidate2.priority -= 1;
7686        remote_candidate2.foundation = "ipv6".to_owned();
7687        state
7688            .local_list()
7689            .add_remote_candidate(remote_candidate.clone());
7690
7691        // adding one local and one remote candidate that can be paired should have generated
7692        // the relevant waiting check. Not frozen because there is not other check with the
7693        // same foundation that already exists
7694        let ipv4_pair = CandidatePair::new(
7695            state.local.peer.candidate.clone(),
7696            state.remote.candidate.clone(),
7697        );
7698        let ipv6_pair = CandidatePair::new(local_candidate2.clone(), remote_candidate2.clone());
7699        let check = state
7700            .local_list()
7701            .matching_check(&ipv4_pair, Nominate::False)
7702            .unwrap();
7703        assert_eq!(check.state(), CandidatePairState::Waiting);
7704        let check_id = check.conncheck_id;
7705
7706        let CheckListSetPollRet::Event {
7707            checklist_id: _,
7708            event: ConnCheckEvent::ComponentState(_cid, ComponentConnectionState::Connecting),
7709        } = state.local.checklist_set.poll(now)
7710        else {
7711            unreachable!();
7712        };
7713
7714        // perform one tick which will start a connectivity check with the peer
7715        send_next_check_and_response(&state.local.peer, &state.remote)
7716            .perform(&mut state.local.checklist_set, now);
7717        let check = state.local_list().check_by_id(check_id).unwrap();
7718        assert_eq!(check.state(), CandidatePairState::Succeeded);
7719
7720        state.local_list().dump_check_state();
7721
7722        let now = wait_advance(&mut state.local.checklist_set, now);
7723
7724        let mut local_auth = ShortTermAuth::new();
7725        local_auth.set_credentials(
7726            state.local.peer.local_credentials.clone().unwrap().into(),
7727            IntegrityAlgorithm::Sha1,
7728        );
7729        let mut remote_agent = StunAgent::builder(
7730            state.remote.candidate.transport_type,
7731            state.remote.candidate.base_address,
7732        )
7733        .build();
7734        let transmit = remote_generate_check(
7735            &state.remote,
7736            &mut remote_agent,
7737            state.local.peer.candidate.address,
7738            true,
7739            true,
7740            now,
7741        );
7742        let reply =
7743            state
7744                .local
7745                .checklist_set
7746                .incoming_data(state.local.checklist_id, 1, transmit, now);
7747        assert!(reply.handled);
7748        let Some(transmit) = state.local.checklist_set.poll_transmit(now) else {
7749            unreachable!();
7750        };
7751
7752        let response = Message::from_bytes(&transmit.transmit.data).unwrap();
7753        trace!("received: {response}");
7754        assert!(matches!(
7755            local_auth.validate_incoming_message(&response).unwrap(),
7756            Some(IntegrityAlgorithm::Sha1)
7757        ));
7758        assert!(remote_agent.handle_stun_message(&response, transmit.transmit.from));
7759        assert_eq!(transmit.transmit.from, state.local.peer.candidate.address);
7760        assert!(response.has_class(MessageClass::Success));
7761
7762        let now = wait_advance(&mut state.local.checklist_set, now);
7763        state.check_nomination(&ipv4_pair, now);
7764
7765        let recv = [8; 9];
7766        state.recv_data(recv, now);
7767
7768        // peer renominates a different candidate
7769        state
7770            .local_list()
7771            .add_remote_candidate(remote_candidate2.clone());
7772        let mut remote_agent =
7773            StunAgent::builder(state.remote.candidate.transport_type, remote_ipv6_addr).build();
7774        let transmit = remote_generate_check(
7775            &state.remote,
7776            &mut remote_agent,
7777            local_ipv6_addr,
7778            true,
7779            true,
7780            now,
7781        );
7782        let reply =
7783            state
7784                .local
7785                .checklist_set
7786                .incoming_data(state.local.checklist_id, 1, transmit, now);
7787        assert!(reply.handled);
7788        let Some(transmit) = state.local.checklist_set.poll_transmit(now) else {
7789            unreachable!();
7790        };
7791
7792        let response = Message::from_bytes(&transmit.transmit.data).unwrap();
7793        trace!("received: {response}");
7794        assert!(matches!(
7795            local_auth.validate_incoming_message(&response).unwrap(),
7796            Some(IntegrityAlgorithm::Sha1)
7797        ));
7798        assert!(remote_agent.handle_stun_message(&response, transmit.transmit.from));
7799        assert_eq!(transmit.transmit.from, local_candidate2.address);
7800        assert!(response.has_class(MessageClass::Success));
7801
7802        let recv = [12; 5];
7803        state.recv_data_with_pair(&ipv6_pair, recv, now);
7804
7805        state.local.checklist_set.close(now);
7806
7807        let CheckListSetPollRet::RemoveSocket {
7808            checklist_id: _,
7809            component_id: 1,
7810            transport: TransportType::Udp,
7811            local_addr,
7812            remote_addr: _,
7813        } = state.local.checklist_set.poll(now)
7814        else {
7815            unreachable!();
7816        };
7817        assert_eq!(local_addr, ipv4_pair.local.base_address);
7818        let CheckListSetPollRet::RemoveSocket {
7819            checklist_id: _,
7820            component_id: 1,
7821            transport: TransportType::Udp,
7822            local_addr,
7823            remote_addr: _,
7824        } = state.local.checklist_set.poll(now)
7825        else {
7826            unreachable!();
7827        };
7828        assert_eq!(local_addr, ipv6_pair.local.base_address);
7829        let CheckListSetPollRet::Closed = state.local.checklist_set.poll(now) else {
7830            unreachable!();
7831        };
7832    }
7833}