1use std::net::SocketAddr;
27use std::time::Duration;
28
29use sipx_sdp::ice::{Candidate, CandidateType, ComponentId, Credentials, Priority};
30
31use super::candidate::{
32 CandidateIds, Foundations, Gathered, LocalBase, LocalCandidate, LocalId, PairFoundation,
33 RemoteCandidate, RemoteId, assign_local_preferences, find_local, find_remote,
34};
35use super::checklist::{
36 CandidatePair, Checklist, ChecklistSet, ChecklistState, PairId, PairIds, PairState, Role,
37 ValidPair, form_pairs, ordered_pair_priority,
38};
39use super::stun::{self, Class, Message, Peering, RoleAttribute, TransactionId};
40use super::timing::Timers;
41
42pub const DEFAULT_PAIR_LIMIT: usize = 100;
44
45const TRANSPORT: sipx_sdp::ice::Transport = sipx_sdp::ice::Transport::Udp;
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub enum Timer {
57 Ta,
59 Retransmit(PairId),
61 Nomination,
66 Keepalive,
68}
69
70#[derive(Debug, Clone)]
72pub enum Input {
73 RemoteDescription {
75 credentials: Credentials,
77 candidates: Vec<Candidate>,
80 lite: bool,
83 },
84 LocalCredentials {
94 credentials: Credentials,
96 tiebreaker: u64,
99 },
100 LocalCandidate(Gathered),
102 GatheringDone,
104 Datagram {
106 from: SocketAddr,
108 on: LocalBase,
110 bytes: Vec<u8>,
112 },
113 DataSent {
115 component: ComponentId,
117 },
118 TimerFired(Timer),
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
128pub enum Output {
129 Send {
131 on: LocalBase,
133 to: SocketAddr,
135 bytes: Vec<u8>,
137 },
138 SetTimer {
140 timer: Timer,
142 after: Duration,
144 },
145 ClearTimer(Timer),
147 Selected {
149 component: ComponentId,
151 local: LocalBase,
153 local_kind: CandidateType,
155 remote: SocketAddr,
157 remote_kind: CandidateType,
159 },
160 Failed {
162 component: ComponentId,
164 },
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169pub struct Config {
170 pub timers: Timers,
174 pub pair_limit: usize,
177}
178
179impl Default for Config {
180 fn default() -> Self {
181 Self {
182 timers: Timers::default(),
183 pair_limit: DEFAULT_PAIR_LIMIT,
184 }
185 }
186}
187
188#[derive(Debug, Clone)]
190struct Transaction {
191 id: TransactionId,
192 pair: PairId,
193 on: LocalBase,
194 from: SocketAddr,
196 to: SocketAddr,
198 bytes: Vec<u8>,
201 priority: Priority,
204 role: RoleAttribute,
206 nominating: bool,
208 attempt: u32,
209 rto: Duration,
210 initial_rto: Duration,
211 final_wait: bool,
212 cancelled: bool,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219enum Conflict {
220 None,
222 Switched,
224 Reject,
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
233enum Phase {
234 Gathering,
236 Gathered,
238 Checking,
240}
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250enum Stopping {
251 Idle,
253 Armed,
255 Elapsed,
257}
258
259#[derive(Debug, Clone, PartialEq, Eq)]
261struct Selection {
262 component: ComponentId,
263 local: LocalBase,
264 remote: SocketAddr,
265 priority: u64,
266}
267
268#[derive(Debug)]
273pub struct Agent {
274 config: Config,
275 role: Role,
276 tiebreaker: u64,
277 offerer: bool,
278 credentials: Credentials,
279 peering: Option<Peering>,
280 local: Vec<LocalCandidate>,
281 remote: Vec<RemoteCandidate>,
282 foundations: Foundations,
283 ids: PairIds,
284 candidate_ids: CandidateIds,
285 ta_armed: bool,
293 set: ChecklistSet,
294 transactions: Vec<Transaction>,
295 phase: Phase,
296 nominating: Vec<(ComponentId, PairId)>,
300 nominate_on_success: Vec<PairId>,
303 stopping: Stopping,
307 selected: Vec<Selection>,
308 failed: Vec<ComponentId>,
309}
310
311impl Agent {
312 #[must_use]
326 pub fn new(config: Config, offerer: bool, credentials: Credentials, tiebreaker: u64) -> Self {
327 Self {
328 config,
329 role: Role::determine(offerer, false),
330 tiebreaker,
331 offerer,
332 credentials,
333 peering: None,
334 local: Vec::new(),
335 remote: Vec::new(),
336 foundations: Foundations::default(),
337 ids: PairIds::default(),
338 candidate_ids: CandidateIds::default(),
339 ta_armed: false,
340 set: ChecklistSet::new(),
341 transactions: Vec::new(),
342 phase: Phase::Gathering,
343 nominating: Vec::new(),
344 nominate_on_success: Vec::new(),
345 stopping: Stopping::Idle,
346 selected: Vec::new(),
347 failed: Vec::new(),
348 }
349 }
350
351 #[must_use]
353 pub const fn role(&self) -> Role {
354 self.role
355 }
356
357 #[must_use]
359 pub const fn tiebreaker(&self) -> u64 {
360 self.tiebreaker
361 }
362
363 #[must_use]
371 pub const fn credentials(&self) -> &Credentials {
372 &self.credentials
373 }
374
375 #[cfg(feature = "dtls")]
377 pub(crate) const fn peering(&self) -> Option<&Peering> {
378 self.peering.as_ref()
379 }
380
381 #[must_use]
383 pub const fn checklists(&self) -> &ChecklistSet {
384 &self.set
385 }
386
387 #[must_use]
389 pub fn local_candidates(&self) -> &[LocalCandidate] {
390 &self.local
391 }
392
393 #[must_use]
395 pub fn remote_candidates(&self) -> &[RemoteCandidate] {
396 &self.remote
397 }
398
399 #[must_use]
401 pub fn selected(&self, component: ComponentId) -> Option<(LocalBase, SocketAddr)> {
402 self.selected
403 .iter()
404 .find(|selection| selection.component == component)
405 .map(|selection| (selection.local, selection.remote))
406 }
407
408 pub fn handle(&mut self, input: Input) -> Vec<Output> {
410 let mut out = Vec::new();
411 match input {
412 Input::RemoteDescription {
413 credentials,
414 candidates,
415 lite,
416 } => self.remote_description(credentials, &candidates, lite, &mut out),
417 Input::LocalCredentials {
418 credentials,
419 tiebreaker,
420 } => {
421 self.credentials = credentials;
422 self.tiebreaker = tiebreaker;
423 }
424 Input::LocalCandidate(gathered) => self.local_candidate(gathered),
425 Input::GatheringDone => {
426 self.phase = self.phase.max(Phase::Gathered);
427 self.start(&mut out);
428 }
429 Input::Datagram { from, on, bytes } => self.datagram(from, on, &bytes, &mut out),
430 Input::DataSent { component } => {
431 if self.selected(component).is_some() {
432 out.push(Output::SetTimer {
433 timer: Timer::Keepalive,
434 after: self.config.timers.tr,
435 });
436 }
437 }
438 Input::TimerFired(timer) => self.timer(timer, &mut out),
439 }
440 out
441 }
442
443 fn remote_description(
462 &mut self,
463 credentials: Credentials,
464 candidates: &[Candidate],
465 lite: bool,
466 out: &mut Vec<Output>,
467 ) {
468 self.role = Role::determine(self.offerer, lite);
471 let restart = self.peering.as_ref().is_some_and(|peering| {
472 let known = peering.remote();
473 known.ufrag() != credentials.ufrag() && known.pwd() != credentials.pwd()
474 });
475 self.peering = Some(Peering::new(self.credentials.clone(), credentials));
476 if restart {
477 self.restart(out);
478 }
479 let added = self.merge_remote_candidates(candidates);
480 if self.phase == Phase::Checking {
481 if added > 0 {
482 self.extend(out);
483 }
484 } else {
485 self.start(out);
486 }
487 }
488
489 fn merge_remote_candidates(&mut self, candidates: &[Candidate]) -> usize {
496 let mut added = 0usize;
497 for candidate in candidates {
498 let Some(parsed) = RemoteCandidate::signalled(RemoteId(0), candidate) else {
499 continue;
500 };
501 if let Some(known) = self
502 .remote
503 .iter_mut()
504 .find(|known| known.address == parsed.address)
505 {
506 known.foundation = parsed.foundation;
509 known.kind = parsed.kind;
510 known.priority = parsed.priority;
511 continue;
512 }
513 let id = self.candidate_ids.remote();
514 self.remote.push(RemoteCandidate { id, ..parsed });
515 added = added.saturating_add(1);
516 }
517 added
518 }
519
520 fn restart(&mut self, out: &mut Vec<Output>) {
522 for transaction in std::mem::take(&mut self.transactions) {
523 out.push(Output::ClearTimer(Timer::Retransmit(transaction.pair)));
524 }
525 self.remote.clear();
526 self.set = ChecklistSet::new();
527 self.nominating.clear();
528 self.nominate_on_success.clear();
529 self.stopping = Stopping::Idle;
530 self.failed.clear();
531 self.phase = self.phase.min(Phase::Gathered);
532 if self.ta_armed {
533 self.ta_armed = false;
534 out.push(Output::ClearTimer(Timer::Ta));
535 }
536 }
539
540 fn extend(&mut self, out: &mut Vec<Output>) {
546 let paired: Vec<RemoteId> = self
547 .set
548 .checklists()
549 .iter()
550 .flat_map(|list| list.pairs().iter().map(|pair| pair.remote))
551 .collect();
552 let fresh: Vec<RemoteCandidate> = self
553 .remote
554 .iter()
555 .filter(|candidate| !paired.contains(&candidate.id))
556 .cloned()
557 .collect();
558 if fresh.is_empty() {
559 return;
560 }
561 let pairs = form_pairs(&mut self.ids, self.role, &self.local, &fresh);
562 for pair in pairs {
563 self.insert_pair(pair);
564 }
565 self.set.limit(self.config.pair_limit);
566 self.forget_unreferenced_remotes();
567 self.set.unfreeze_added();
568 self.arm_ta(out);
569 }
570
571 fn local_candidate(&mut self, gathered: Gathered) {
572 let foundation = self.foundations.assign(&gathered, TRANSPORT);
573 self.local.push(LocalCandidate {
574 id: self.candidate_ids.local(),
575 gathered,
576 foundation,
577 local_preference: 0,
578 priority: Priority::MIN,
579 });
580 assign_local_preferences(&mut self.local);
583 }
584
585 fn start(&mut self, out: &mut Vec<Output>) {
587 if self.phase != Phase::Gathered || self.peering.is_none() {
588 return;
589 }
590 if self.local.is_empty() || self.remote.is_empty() {
591 return;
592 }
593 self.phase = Phase::Checking;
594 let pairs = form_pairs(&mut self.ids, self.role, &self.local, &self.remote);
595 self.set = ChecklistSet::new();
596 self.set.push(Checklist::new(pairs));
597 self.set.limit(self.config.pair_limit);
598 self.set.compute_initial_states();
599 self.arm_ta(out);
600 }
601
602 fn arm_ta(&mut self, out: &mut Vec<Output>) {
608 if self.ta_armed {
609 return;
610 }
611 self.ta_armed = true;
612 out.push(Output::SetTimer {
613 timer: Timer::Ta,
614 after: self.config.timers.pacing(),
615 });
616 }
617
618 fn insert_pair(&mut self, pair: CandidatePair) {
621 let Some((base, remote)) = find_local(&self.local, pair.local)
622 .map(|local| local.gathered.base_address)
623 .zip(find_remote(&self.remote, pair.remote).map(|remote| remote.address))
624 else {
625 return;
626 };
627 let redundant = self.set.checklists().iter().any(|list| {
628 list.pairs().iter().any(|known| {
629 find_local(&self.local, known.local)
630 .is_some_and(|local| local.gathered.base_address == base)
631 && find_remote(&self.remote, known.remote)
632 .is_some_and(|known| known.address == remote)
633 })
634 });
635 if redundant {
636 return;
637 }
638 if let Some(list) = self.set.checklists_mut().first_mut() {
639 list.insert(pair);
640 }
641 }
642
643 fn forget_unreferenced_remotes(&mut self) {
649 let mut live: Vec<RemoteId> = self
650 .set
651 .checklists()
652 .iter()
653 .flat_map(|list| list.pairs().iter().map(|pair| pair.remote))
654 .collect();
655 let addresses: Vec<SocketAddr> = self
656 .set
657 .checklists()
658 .iter()
659 .flat_map(|list| list.valid().iter().map(|valid| valid.remote))
660 .chain(self.selected.iter().map(|selection| selection.remote))
661 .collect();
662 for candidate in &self.remote {
663 if addresses.contains(&candidate.address) {
664 live.push(candidate.id);
665 }
666 }
667 self.remote.retain(|candidate| {
668 candidate.kind != CandidateType::PeerReflexive || live.contains(&candidate.id)
669 });
670 }
671
672 fn timer(&mut self, timer: Timer, out: &mut Vec<Output>) {
675 match timer {
676 Timer::Ta => self.pace(out),
677 Timer::Retransmit(pair) => self.retransmit(pair, out),
678 Timer::Nomination => {
679 self.stopping = Stopping::Elapsed;
680 self.nominate();
681 }
682 Timer::Keepalive => self.keepalive(out),
683 }
684 }
685
686 fn pace(&mut self, out: &mut Vec<Output>) {
688 self.ta_armed = false;
690 let count = self.set.checklists().len();
691 for _ in 0..count {
692 let Some(index) = self.set.next_active() else {
693 break;
694 };
695 let triggered = self
698 .set
699 .checklists_mut()
700 .get_mut(index)
701 .and_then(Checklist::take_triggered);
702 if let Some(id) = triggered {
703 self.send_check(id, out);
704 break;
705 }
706 if self
707 .set
708 .checklists()
709 .get(index)
710 .is_some_and(|list| list.state() != ChecklistState::Running)
711 {
712 continue;
715 }
716 self.set.unfreeze_idle(index);
718 let waiting = self.set.checklists().get(index).and_then(|list| {
720 list.pairs()
721 .iter()
722 .filter(|pair| pair.state == PairState::Waiting)
723 .max_by_key(|pair| (pair.priority, std::cmp::Reverse(pair.component)))
724 .map(|pair| pair.id)
725 });
726 if let Some(id) = waiting {
727 self.send_check(id, out);
728 break;
729 }
730 }
732 if self.active() {
733 self.arm_ta(out);
734 }
735 }
736
737 fn active(&self) -> bool {
746 self.set
747 .checklists()
748 .iter()
749 .any(|list| list.state() == ChecklistState::Running || list.has_triggered())
750 }
751
752 fn retransmit(&mut self, pair: PairId, out: &mut Vec<Output>) {
755 let Some(position) = self
756 .transactions
757 .iter()
758 .position(|transaction| transaction.pair == pair)
759 else {
760 return;
761 };
762 let Some(transaction) = self.transactions.get_mut(position) else {
763 return;
764 };
765 if transaction.cancelled {
766 self.transactions.remove(position);
769 return;
770 }
771 if transaction.attempt < self.config.timers.rc {
772 transaction.attempt = transaction.attempt.saturating_add(1);
773 transaction.rto = self.config.timers.double(transaction.rto);
774 let (on, to, bytes, after) = (
775 transaction.on,
776 transaction.to,
777 transaction.bytes.clone(),
778 transaction.rto,
779 );
780 out.push(Output::Send { on, to, bytes });
781 out.push(Output::SetTimer {
782 timer: Timer::Retransmit(pair),
783 after,
784 });
785 return;
786 }
787 if !transaction.final_wait {
788 transaction.final_wait = true;
789 let after = self.config.timers.final_wait(transaction.initial_rto);
790 out.push(Output::SetTimer {
791 timer: Timer::Retransmit(pair),
792 after,
793 });
794 return;
795 }
796 let nominating = transaction.nominating;
798 self.transactions.remove(position);
799 self.fail_pair(pair, nominating, out);
800 }
801
802 fn keepalive(&mut self, out: &mut Vec<Output>) {
804 for selection in &self.selected {
805 if let Ok(bytes) = stun::keepalive(stun::new_transaction_id()) {
806 out.push(Output::Send {
807 on: selection.local,
808 to: selection.remote,
809 bytes,
810 });
811 }
812 }
813 if !self.selected.is_empty() {
814 out.push(Output::SetTimer {
815 timer: Timer::Keepalive,
816 after: self.config.timers.tr,
817 });
818 }
819 }
820
821 fn send_check(&mut self, id: PairId, out: &mut Vec<Output>) {
824 let Some(peering) = self.peering.clone() else {
825 return;
826 };
827 let Some(pair) = self.set.pair(id) else {
828 return;
829 };
830 let (local_id, remote_id, component) = (pair.local, pair.remote, pair.component);
831 let (Some(local), Some(remote)) = (
832 find_local(&self.local, local_id),
833 find_remote(&self.remote, remote_id),
834 ) else {
835 return;
836 };
837 let (on, from, to) = (
838 local.gathered.base,
839 local.gathered.base_address,
840 remote.address,
841 );
842 let check_priority = local.check_priority();
844 let nominating = self.role.is_controlling()
845 && self
846 .nominating
847 .iter()
848 .any(|(nominated, pair)| *nominated == component && *pair == id);
849 let role = match self.role {
850 Role::Controlling => RoleAttribute::Controlling {
851 tiebreaker: self.tiebreaker,
852 nominate: nominating,
853 },
854 Role::Controlled => RoleAttribute::Controlled {
855 tiebreaker: self.tiebreaker,
856 },
857 };
858 let transaction_id = stun::new_transaction_id();
859 let Ok(bytes) = stun::connectivity_check(transaction_id, &peering, check_priority, role)
860 else {
861 return;
862 };
863
864 if let Some(pair) = self.set.pair_mut(id) {
865 pair.state = PairState::InProgress;
866 }
867 let rto = self
870 .config
871 .timers
872 .rto(self.set.total_pairs(), self.set.outstanding());
873
874 out.push(Output::Send {
875 on,
876 to,
877 bytes: bytes.clone(),
878 });
879 out.push(Output::SetTimer {
880 timer: Timer::Retransmit(id),
881 after: rto,
882 });
883 self.transactions
884 .retain(|transaction| transaction.pair != id);
885 self.transactions.push(Transaction {
886 id: transaction_id,
887 pair: id,
888 on,
889 from,
890 to,
891 bytes,
892 priority: check_priority,
893 role,
894 nominating,
895 attempt: 1,
896 rto,
897 initial_rto: rto,
898 final_wait: false,
899 cancelled: false,
900 });
901 }
902
903 fn datagram(&mut self, from: SocketAddr, on: LocalBase, bytes: &[u8], out: &mut Vec<Output>) {
906 let Ok(message) = Message::decode(bytes) else {
907 return;
909 };
910 match message.class() {
911 Class::Request => self.inbound_check(from, on, &message, out),
912 Class::Success | Class::Error => self.inbound_response(from, on, &message, out),
913 Class::Indication => {}
915 }
916 }
917
918 fn inbound_check(
920 &mut self,
921 from: SocketAddr,
922 on: LocalBase,
923 message: &Message,
924 out: &mut Vec<Output>,
925 ) {
926 let Some(peering) = self.peering.clone() else {
927 return;
928 };
929 if message.username() != Some(peering.inbound_username().as_str())
934 || !message.verify_integrity(peering.inbound_key())
935 {
936 return;
937 }
938
939 match self.resolve_conflict(message.role()) {
940 Conflict::Reject => {
941 if let Ok(bytes) = stun::role_conflict(message.transaction(), &peering) {
942 out.push(Output::Send {
943 on,
944 to: from,
945 bytes,
946 });
947 }
948 return;
949 }
950 Conflict::Switched | Conflict::None => {}
951 }
952
953 if let Ok(bytes) = stun::check_success(message.transaction(), &peering, from) {
956 out.push(Output::Send {
957 on,
958 to: from,
959 bytes,
960 });
961 }
962 if self.phase != Phase::Checking {
963 return;
966 }
967
968 let Some(local_id) = self.base_candidate(on) else {
969 return;
970 };
971 let Some(component) =
972 find_local(&self.local, local_id).map(|candidate| candidate.gathered.component)
973 else {
974 return;
975 };
976 let remote_id = self.learn_remote(from, component, message.priority());
977 self.triggered_check(local_id, remote_id, component, message.use_candidate(), out);
978 }
979
980 fn learn_remote(
983 &mut self,
984 from: SocketAddr,
985 component: ComponentId,
986 claimed: Option<Priority>,
987 ) -> RemoteId {
988 if let Some(known) = self
989 .remote
990 .iter()
991 .find(|candidate| candidate.address == from)
992 {
993 return known.id;
994 }
995 let id = self.candidate_ids.remote();
996 self.remote.push(RemoteCandidate {
997 id,
998 address: from,
999 kind: CandidateType::PeerReflexive,
1000 component,
1001 foundation: self.foundations.learn_remote(),
1003 priority: claimed.unwrap_or(Priority::MIN),
1007 });
1008 id
1009 }
1010
1011 fn triggered_check(
1013 &mut self,
1014 local: LocalId,
1015 remote: RemoteId,
1016 component: ComponentId,
1017 use_candidate: bool,
1018 out: &mut Vec<Output>,
1019 ) {
1020 let existing = self
1021 .set
1022 .checklists()
1023 .iter()
1024 .find_map(|list| list.find(local, remote));
1025 let id = if let Some(id) = existing {
1026 id
1027 } else {
1028 let Some(pair) = self.build_pair(local, remote, component) else {
1031 return;
1032 };
1033 let id = pair.id;
1034 self.insert_pair(pair);
1035 if let Some(pair) = self.set.pair_mut(id) {
1036 pair.state = PairState::Waiting;
1037 }
1038 self.set.limit(self.config.pair_limit);
1044 self.forget_unreferenced_remotes();
1045 if self.set.pair(id).is_none() {
1046 return;
1050 }
1051 if let Some(index) = self.set.checklist_of(id)
1052 && let Some(list) = self.set.checklists_mut().get_mut(index)
1053 {
1054 list.trigger(id);
1055 }
1056 self.arm_ta(out);
1057 id
1058 };
1059 let before = self.set.pair(id).map(|pair| pair.state);
1060 match before {
1061 Some(PairState::Succeeded) => {
1062 }
1064 Some(_) if self.set.pair(id).is_some_and(|pair| pair.nominated) => {
1065 }
1076 Some(state) => {
1077 if state == PairState::InProgress {
1078 if let Some(transaction) = self
1081 .transactions
1082 .iter_mut()
1083 .find(|transaction| transaction.pair == id)
1084 {
1085 transaction.cancelled = true;
1086 }
1087 }
1088 if let Some(pair) = self.set.pair_mut(id) {
1089 pair.state = PairState::Waiting;
1090 }
1091 if let Some(index) = self.set.checklist_of(id)
1092 && let Some(list) = self.set.checklists_mut().get_mut(index)
1093 {
1094 list.trigger(id);
1095 }
1096 self.arm_ta(out);
1099 }
1100 None => return,
1101 }
1102
1103 if use_candidate && !self.role.is_controlling() {
1104 if before == Some(PairState::Succeeded) {
1107 self.mark_nominated(id, out);
1108 } else if !self.nominate_on_success.contains(&id) {
1109 self.nominate_on_success.push(id);
1110 }
1111 }
1112 }
1113
1114 fn build_pair(
1115 &mut self,
1116 local: LocalId,
1117 remote: RemoteId,
1118 component: ComponentId,
1119 ) -> Option<CandidatePair> {
1120 let local_candidate = find_local(&self.local, local)?;
1121 let remote_candidate = find_remote(&self.remote, remote)?;
1122 Some(CandidatePair {
1123 id: self.ids.allocate(),
1124 local,
1125 remote,
1126 component,
1127 foundation: PairFoundation {
1128 local: local_candidate.foundation,
1129 remote: remote_candidate.foundation.clone(),
1130 },
1131 priority: ordered_pair_priority(
1132 self.role,
1133 local_candidate.priority,
1134 remote_candidate.priority,
1135 ),
1136 state: PairState::Frozen,
1137 nominated: false,
1138 })
1139 }
1140
1141 fn base_candidate(&self, on: LocalBase) -> Option<LocalId> {
1142 self.local
1143 .iter()
1144 .find(|candidate| {
1145 candidate.gathered.base == on && candidate.gathered.kind == CandidateType::Host
1146 })
1147 .map(|candidate| candidate.id)
1148 }
1149
1150 fn inbound_response(
1153 &mut self,
1154 from: SocketAddr,
1155 on: LocalBase,
1156 message: &Message,
1157 out: &mut Vec<Output>,
1158 ) {
1159 let Some(peering) = self.peering.clone() else {
1160 return;
1161 };
1162 let Some(position) = self
1163 .transactions
1164 .iter()
1165 .position(|transaction| transaction.id == message.transaction())
1166 else {
1167 return;
1168 };
1169 let Some(transaction) = self.transactions.get(position).cloned() else {
1170 return;
1171 };
1172 if !message.verify_integrity(peering.outbound_key()) {
1175 return;
1176 }
1177 if from != transaction.to || on != transaction.on {
1180 self.transactions.remove(position);
1181 out.push(Output::ClearTimer(Timer::Retransmit(transaction.pair)));
1182 self.fail_pair(transaction.pair, transaction.nominating, out);
1183 return;
1184 }
1185
1186 self.transactions.remove(position);
1187 out.push(Output::ClearTimer(Timer::Retransmit(transaction.pair)));
1188
1189 if message.class() == Class::Error {
1190 if message.error_code() == Some(stun::ROLE_CONFLICT) {
1191 self.role_conflict_response(&transaction);
1192 } else {
1193 self.fail_pair(transaction.pair, transaction.nominating, out);
1195 }
1196 return;
1197 }
1198
1199 self.success(&transaction, message, out);
1200 }
1201
1202 fn success(&mut self, transaction: &Transaction, message: &Message, out: &mut Vec<Output>) {
1204 let Some(pair) = self.set.pair(transaction.pair).cloned() else {
1205 return;
1206 };
1207 let mapped = message.mapped_address().unwrap_or(transaction.from);
1211 let local = self.learn_local(mapped, &pair, transaction.priority);
1212
1213 let priority = find_local(&self.local, local)
1216 .zip(find_remote(&self.remote, pair.remote))
1217 .map_or(pair.priority, |(local, remote)| {
1218 ordered_pair_priority(self.role, local.priority, remote.priority)
1219 });
1220 let first_valid = if let Some(index) = self.set.checklist_of(transaction.pair) {
1221 self.set
1222 .checklists_mut()
1223 .get_mut(index)
1224 .is_some_and(|list| {
1225 list.add_valid(ValidPair {
1226 component: pair.component,
1227 local,
1228 remote: transaction.to,
1229 priority,
1230 nominated: false,
1231 generated_by: pair.id,
1232 })
1233 })
1234 } else {
1235 false
1236 };
1237
1238 if let Some(pair) = self.set.pair_mut(transaction.pair) {
1240 pair.state = PairState::Succeeded;
1241 }
1242 self.set.unfreeze_foundation(&pair.foundation);
1243
1244 if transaction.nominating || self.nominate_on_success.contains(&transaction.pair) {
1247 self.nominate_on_success
1248 .retain(|id| *id != transaction.pair);
1249 self.mark_nominated(transaction.pair, out);
1250 }
1251
1252 if first_valid && self.stopping == Stopping::Idle {
1253 self.stopping = Stopping::Armed;
1255 out.push(Output::SetTimer {
1256 timer: Timer::Nomination,
1257 after: self.config.timers.tn,
1258 });
1259 }
1260
1261 self.update_checklists(out);
1262 self.nominate();
1263 }
1264
1265 fn learn_local(
1267 &mut self,
1268 mapped: SocketAddr,
1269 pair: &CandidatePair,
1270 claimed: Priority,
1271 ) -> LocalId {
1272 if let Some(known) = self
1273 .local
1274 .iter()
1275 .find(|candidate| candidate.gathered.address == mapped)
1276 {
1277 return known.id;
1278 }
1279 let Some(base) = find_local(&self.local, pair.local).copied() else {
1280 return pair.local;
1281 };
1282 let gathered = Gathered {
1283 base: base.gathered.base,
1284 base_address: base.gathered.base_address,
1285 address: mapped,
1286 kind: CandidateType::PeerReflexive,
1287 component: pair.component,
1288 server: None,
1289 };
1290 let foundation = self.foundations.assign(&gathered, TRANSPORT);
1291 let id = self.candidate_ids.local();
1292 self.local.push(LocalCandidate {
1293 id,
1294 gathered,
1295 foundation,
1296 local_preference: base.local_preference,
1297 priority: claimed,
1301 });
1302 id
1303 }
1304
1305 fn fail_pair(&mut self, id: PairId, nominating: bool, out: &mut Vec<Output>) {
1306 if let Some(pair) = self.set.pair_mut(id) {
1307 pair.state = PairState::Failed;
1308 }
1309 if nominating {
1310 if let Some(index) = self.set.checklist_of(id)
1313 && let Some(list) = self.set.checklists_mut().get_mut(index)
1314 {
1315 list.remove_valid(id);
1316 list.set_state(ChecklistState::Failed);
1317 }
1318 }
1319 self.update_checklists(out);
1320 }
1321
1322 fn resolve_conflict(&mut self, attribute: Option<RoleAttribute>) -> Conflict {
1326 let Some(attribute) = attribute else {
1327 return Conflict::None;
1329 };
1330 let theirs = attribute.tiebreaker();
1331 match (self.role, attribute) {
1332 (Role::Controlling, RoleAttribute::Controlling { .. }) => {
1333 if self.tiebreaker >= theirs {
1334 Conflict::Reject
1335 } else {
1336 self.switch_role();
1337 Conflict::Switched
1338 }
1339 }
1340 (Role::Controlled, RoleAttribute::Controlled { .. }) => {
1341 if self.tiebreaker >= theirs {
1342 self.switch_role();
1343 Conflict::Switched
1344 } else {
1345 Conflict::Reject
1346 }
1347 }
1348 _ => Conflict::None,
1350 }
1351 }
1352
1353 fn role_conflict_response(&mut self, transaction: &Transaction) {
1355 self.role = match transaction.role {
1359 RoleAttribute::Controlled { .. } => Role::Controlling,
1360 RoleAttribute::Controlling { .. } => Role::Controlled,
1361 };
1362 self.tiebreaker = fresh_tiebreaker(self.tiebreaker);
1364 self.set
1365 .recompute_priorities(self.role, &self.local, &self.remote);
1366 if let Some(pair) = self.set.pair_mut(transaction.pair) {
1367 pair.state = PairState::Waiting;
1368 }
1369 if let Some(index) = self.set.checklist_of(transaction.pair)
1370 && let Some(list) = self.set.checklists_mut().get_mut(index)
1371 {
1372 list.trigger(transaction.pair);
1373 }
1374 }
1375
1376 fn switch_role(&mut self) {
1377 self.role = self.role.opposite();
1378 self.set
1381 .recompute_priorities(self.role, &self.local, &self.remote);
1382 }
1383
1384 fn nominate(&mut self) {
1405 if !self.role.is_controlling() {
1406 return;
1407 }
1408 let mut queue: Vec<(usize, ComponentId, PairId)> = Vec::new();
1409 for (index, list) in self.set.checklists().iter().enumerate() {
1410 if list.state() != ChecklistState::Running {
1411 continue;
1412 }
1413 let components = list.components();
1414 if components.is_empty() {
1415 continue;
1416 }
1417 let best: Vec<(ComponentId, &ValidPair)> = components
1419 .iter()
1420 .filter_map(|component| {
1421 list.valid()
1422 .iter()
1423 .filter(|valid| valid.component == *component)
1424 .max_by_key(|valid| valid.priority)
1425 .map(|valid| (*component, valid))
1426 })
1427 .collect();
1428 if best.len() != components.len() {
1429 continue;
1430 }
1431 let settled = best.iter().all(|(component, valid)| {
1434 list.pairs()
1435 .iter()
1436 .filter(|pair| pair.component == *component && pair.priority > valid.priority)
1437 .all(|pair| pair.state == PairState::Failed)
1438 });
1439 if !settled && self.stopping != Stopping::Elapsed {
1440 continue;
1441 }
1442 for (component, valid) in best {
1443 if self
1444 .nominating
1445 .iter()
1446 .any(|(nominated, _)| *nominated == component)
1447 {
1448 continue;
1449 }
1450 queue.push((index, component, valid.generated_by));
1451 }
1452 }
1453 for (index, component, pair) in queue {
1454 self.nominating.push((component, pair));
1455 if let Some(list) = self.set.checklists_mut().get_mut(index) {
1456 list.trigger(pair);
1457 }
1458 }
1459 }
1460
1461 fn mark_nominated(&mut self, id: PairId, out: &mut Vec<Output>) {
1464 let Some(index) = self.set.checklist_of(id) else {
1465 return;
1466 };
1467 if let Some(pair) = self.set.pair_mut(id) {
1468 pair.nominated = true;
1469 }
1470 if let Some(list) = self.set.checklists_mut().get_mut(index) {
1471 list.nominate_valid(id);
1472 }
1473 self.conclude(index, out);
1474 }
1475
1476 fn conclude(&mut self, index: usize, out: &mut Vec<Output>) {
1479 let Some(list) = self.set.checklists().get(index) else {
1480 return;
1481 };
1482 let components = list.components();
1483 let mut chosen: Vec<(ComponentId, PairId, LocalId, SocketAddr, u64)> = Vec::new();
1484 for component in &components {
1485 if let Some(valid) = list
1489 .valid()
1490 .iter()
1491 .filter(|valid| valid.component == *component && valid.nominated)
1492 .max_by_key(|valid| valid.priority)
1493 {
1494 chosen.push((
1495 *component,
1496 valid.generated_by,
1497 valid.local,
1498 valid.remote,
1499 valid.priority,
1500 ));
1501 }
1502 }
1503
1504 for (component, pair, local, remote, priority) in &chosen {
1505 let Some(local_candidate) = find_local(&self.local, *local) else {
1506 continue;
1507 };
1508 let base = local_candidate.gathered.base;
1509 let local_kind = local_candidate.gathered.kind;
1510 let Some(remote_kind) = self
1511 .set
1512 .pair(*pair)
1513 .and_then(|candidate_pair| find_remote(&self.remote, candidate_pair.remote))
1514 .map(|candidate| candidate.kind)
1515 else {
1516 continue;
1517 };
1518 let selection = Selection {
1519 component: *component,
1520 local: base,
1521 remote: *remote,
1522 priority: *priority,
1523 };
1524 let existing = self
1525 .selected
1526 .iter_mut()
1527 .find(|known| known.component == *component);
1528 match existing {
1529 Some(known) if *known == selection => continue,
1530 Some(known) if known.priority >= *priority => continue,
1531 Some(known) => *known = selection,
1532 None => self.selected.push(selection),
1533 }
1534 out.push(Output::Selected {
1535 component: *component,
1536 local: base,
1537 local_kind,
1538 remote: *remote,
1539 remote_kind,
1540 });
1541 if self.role.is_controlling() {
1546 if let Some(list) = self.set.checklists_mut().get_mut(index) {
1547 list.keep_only_nominated(*component, *pair);
1548 }
1549 let live: Vec<PairId> = self
1555 .set
1556 .checklists()
1557 .iter()
1558 .flat_map(|list| list.pairs().iter().map(|pair| pair.id))
1559 .collect();
1560 for transaction in &self.transactions {
1561 if !live.contains(&transaction.pair) {
1562 out.push(Output::ClearTimer(Timer::Retransmit(transaction.pair)));
1563 }
1564 }
1565 self.transactions
1566 .retain(|transaction| live.contains(&transaction.pair));
1567 }
1568 }
1569
1570 if chosen.len() == components.len() && !components.is_empty() {
1571 if let Some(list) = self.set.checklists_mut().get_mut(index) {
1572 list.set_state(ChecklistState::Completed);
1573 }
1574 if !self.active() {
1575 for transaction in std::mem::take(&mut self.transactions) {
1576 out.push(Output::ClearTimer(Timer::Retransmit(transaction.pair)));
1577 }
1578 self.ta_armed = false;
1579 out.push(Output::ClearTimer(Timer::Ta));
1580 out.push(Output::SetTimer {
1581 timer: Timer::Keepalive,
1582 after: self.config.timers.tr,
1583 });
1584 }
1585 }
1586 }
1587
1588 fn update_checklists(&mut self, out: &mut Vec<Output>) {
1590 let mut failed: Vec<ComponentId> = Vec::new();
1591 for list in self.set.checklists_mut() {
1592 if list.state() != ChecklistState::Running {
1593 continue;
1594 }
1595 let components = list.components();
1596 if components.is_empty() {
1597 continue;
1598 }
1599 let settled = list.pairs().iter().all(|pair| pair.state.is_final());
1600 let covered = components.iter().all(|component| {
1601 list.valid()
1602 .iter()
1603 .any(|valid| valid.component == *component)
1604 });
1605 if settled && !covered {
1606 list.set_state(ChecklistState::Failed);
1607 for component in components {
1608 if !list
1609 .valid()
1610 .iter()
1611 .any(|valid| valid.component == component)
1612 {
1613 failed.push(component);
1614 }
1615 }
1616 }
1617 }
1618 for component in failed {
1619 if self.failed.contains(&component) {
1620 continue;
1621 }
1622 self.failed.push(component);
1623 out.push(Output::Failed { component });
1624 }
1625 }
1626}
1627
1628fn fresh_tiebreaker(previous: u64) -> u64 {
1641 let mut next: u64 = rand::random();
1642 while next == previous {
1643 next = rand::random();
1644 }
1645 next
1646}
1647
1648#[cfg(test)]
1649#[allow(
1650 clippy::unwrap_used,
1651 clippy::expect_used,
1652 clippy::panic,
1653 clippy::indexing_slicing
1654)]
1655mod tests {
1656 use super::*;
1657
1658 const ALICE: &str = "192.0.2.1:5000";
1659 const BOB: &str = "192.0.2.2:5000";
1660
1661 fn address(text: &str) -> SocketAddr {
1662 text.parse().unwrap()
1663 }
1664
1665 fn credentials(ufrag: &str) -> Credentials {
1666 Credentials::new(ufrag, format!("{ufrag}xxxxxxxxxxxxxxxxxxxxxxxx")).unwrap()
1667 }
1668
1669 fn host(address: SocketAddr) -> Gathered {
1670 Gathered {
1671 base: LocalBase(0),
1672 base_address: address,
1673 address,
1674 kind: CandidateType::Host,
1675 component: ComponentId::RTP,
1676 server: None,
1677 }
1678 }
1679
1680 fn host_line(address: SocketAddr, foundation: &str) -> Candidate {
1681 Candidate::parse(&format!(
1682 "{foundation} 1 UDP 1694498815 {} {} typ host",
1685 address.ip(),
1686 address.port()
1687 ))
1688 .unwrap()
1689 }
1690
1691 #[derive(Debug, Default)]
1697 struct Driver {
1698 armed: bool,
1699 }
1700
1701 impl Driver {
1702 fn tick(&mut self, agent: &mut Agent) -> Vec<Output> {
1705 self.armed = false;
1706 let outputs = agent.handle(Input::TimerFired(Timer::Ta));
1707 self.absorb(&outputs);
1708 outputs
1709 }
1710
1711 fn absorb(&mut self, outputs: &[Output]) {
1712 for output in outputs {
1713 match output {
1714 Output::SetTimer {
1715 timer: Timer::Ta, ..
1716 } => self.armed = true,
1717 Output::ClearTimer(Timer::Ta) => self.armed = false,
1718 _ => {}
1719 }
1720 }
1721 }
1722 }
1723
1724 fn two_agents(
1725 offerer: (bool, bool),
1726 tiebreakers: (u64, u64),
1727 ) -> (Agent, Agent, Driver, Driver) {
1728 let (alice_address, bob_address) = (address(ALICE), address(BOB));
1729 let mut alice = Agent::new(
1730 Config::default(),
1731 offerer.0,
1732 credentials("aaaa"),
1733 tiebreakers.0,
1734 );
1735 let mut bob = Agent::new(
1736 Config::default(),
1737 offerer.1,
1738 credentials("bbbb"),
1739 tiebreakers.1,
1740 );
1741 let (mut left, mut right) = (Driver::default(), Driver::default());
1742 left.absorb(&alice.handle(Input::LocalCandidate(host(alice_address))));
1743 right.absorb(&bob.handle(Input::LocalCandidate(host(bob_address))));
1744 left.absorb(&alice.handle(Input::RemoteDescription {
1745 credentials: credentials("bbbb"),
1746 candidates: vec![host_line(bob_address, "1")],
1747 lite: false,
1748 }));
1749 right.absorb(&bob.handle(Input::RemoteDescription {
1750 credentials: credentials("aaaa"),
1751 candidates: vec![host_line(alice_address, "1")],
1752 lite: false,
1753 }));
1754 left.absorb(&alice.handle(Input::GatheringDone));
1755 right.absorb(&bob.handle(Input::GatheringDone));
1756 (alice, bob, left, right)
1757 }
1758
1759 fn both_controlling(tiebreaker: u64) -> (Agent, Agent, Driver, Driver) {
1763 two_agents((true, true), (tiebreaker, tiebreaker))
1764 }
1765
1766 fn exchange(
1771 a: &mut Agent,
1772 b: &mut Agent,
1773 left: &mut Driver,
1774 right: &mut Driver,
1775 rounds: usize,
1776 ) {
1777 let (alice, bob) = (address(ALICE), address(BOB));
1778 for _ in 0..rounds {
1779 let mut pending: Vec<(bool, Vec<u8>)> = Vec::new();
1780 if left.armed {
1781 for output in left.tick(a) {
1782 if let Output::Send { bytes, .. } = output {
1783 pending.push((true, bytes));
1784 }
1785 }
1786 }
1787 if right.armed {
1788 for output in right.tick(b) {
1789 if let Output::Send { bytes, .. } = output {
1790 pending.push((false, bytes));
1791 }
1792 }
1793 }
1794 for _ in 0..8 {
1795 let mut next: Vec<(bool, Vec<u8>)> = Vec::new();
1796 for (to_bob, bytes) in pending {
1797 let (target, driver, from) = if to_bob {
1798 (&mut *b, &mut *right, alice)
1799 } else {
1800 (&mut *a, &mut *left, bob)
1801 };
1802 let outputs = target.handle(Input::Datagram {
1803 from,
1804 on: LocalBase(0),
1805 bytes,
1806 });
1807 driver.absorb(&outputs);
1808 for output in outputs {
1809 if let Output::Send { bytes, .. } = output {
1810 next.push((!to_bob, bytes));
1811 }
1812 }
1813 }
1814 if next.is_empty() {
1815 break;
1816 }
1817 pending = next;
1818 }
1819 }
1820 }
1821
1822 fn agent(offerer: bool, tiebreaker: u64, remotes: &[SocketAddr]) -> Agent {
1823 let mut agent = Agent::new(Config::default(), offerer, credentials("aaaa"), tiebreaker);
1824 agent.handle(Input::LocalCandidate(host(address(ALICE))));
1825 agent.handle(Input::RemoteDescription {
1826 credentials: credentials("bbbb"),
1827 candidates: remotes
1828 .iter()
1829 .enumerate()
1830 .map(|(index, remote)| host_line(*remote, &(index + 1).to_string()))
1831 .collect(),
1832 lite: false,
1833 });
1834 agent.handle(Input::GatheringDone);
1835 agent
1836 }
1837
1838 fn peer() -> Peering {
1841 Peering::new(credentials("bbbb"), credentials("aaaa"))
1842 }
1843
1844 fn sent(outputs: &[Output]) -> Vec<Message> {
1845 outputs
1846 .iter()
1847 .filter_map(|output| match output {
1848 Output::Send { bytes, .. } => Message::decode(bytes).ok(),
1849 _ => None,
1850 })
1851 .collect()
1852 }
1853
1854 fn requests(outputs: &[Output]) -> Vec<Message> {
1855 sent(outputs)
1856 .into_iter()
1857 .filter(|message| message.class() == Class::Request)
1858 .collect()
1859 }
1860
1861 fn retransmit_after(outputs: &[Output]) -> Option<Duration> {
1862 outputs.iter().find_map(|output| match output {
1863 Output::SetTimer {
1864 timer: Timer::Retransmit(_),
1865 after,
1866 } => Some(*after),
1867 _ => None,
1868 })
1869 }
1870
1871 fn peer_check(role: RoleAttribute) -> Vec<u8> {
1873 stun::connectivity_check(
1874 stun::new_transaction_id(),
1875 &peer(),
1876 Priority::new(1_862_270_975).unwrap(),
1877 role,
1878 )
1879 .unwrap()
1880 }
1881
1882 fn deliver(agent: &mut Agent, from: SocketAddr, bytes: Vec<u8>) -> Vec<Output> {
1883 agent.handle(Input::Datagram {
1884 from,
1885 on: LocalBase(0),
1886 bytes,
1887 })
1888 }
1889
1890 #[test]
1899 fn the_agent_reads_no_clock_and_owns_no_socket() {
1900 let code = |source: &str| -> String {
1905 source
1906 .lines()
1907 .filter(|line| !line.trim_start().starts_with("//"))
1908 .collect::<Vec<_>>()
1909 .join("\n")
1910 };
1911 let forbidden = [
1913 ["tok", "io"].concat(),
1914 ["Udp", "Socket"].concat(),
1915 ["Ins", "tant"].concat(),
1916 ["System", "Time"].concat(),
1917 ["std::", "thread"].concat(),
1918 ];
1919 for source in [
1920 code(include_str!("agent.rs")),
1921 code(include_str!("checklist.rs")),
1922 code(include_str!("candidate.rs")),
1923 code(include_str!("timing.rs")),
1924 ] {
1925 for forbidden in &forbidden {
1926 assert!(
1927 !source.contains(forbidden),
1928 "the ICE agent must not reach for {forbidden}: time arrives as TimerFired \
1929 and datagrams arrive as bytes"
1930 );
1931 }
1932 }
1933 }
1934
1935 #[test]
1941 fn the_role_conflict_table_is_walked_row_by_row() {
1942 let controlling = |tiebreaker: u64| RoleAttribute::Controlling {
1943 tiebreaker,
1944 nominate: false,
1945 };
1946 let controlled = |tiebreaker: u64| RoleAttribute::Controlled { tiebreaker };
1947
1948 let mut subject = agent(true, 100, &[address(BOB)]);
1950 assert_eq!(
1951 subject.resolve_conflict(Some(controlling(50))),
1952 Conflict::Reject
1953 );
1954 assert_eq!(subject.role(), Role::Controlling);
1955
1956 let mut subject = agent(true, 100, &[address(BOB)]);
1959 assert_eq!(
1960 subject.resolve_conflict(Some(controlling(100))),
1961 Conflict::Reject
1962 );
1963 assert_eq!(subject.role(), Role::Controlling);
1964
1965 let mut subject = agent(true, 100, &[address(BOB)]);
1967 assert_eq!(
1968 subject.resolve_conflict(Some(controlling(200))),
1969 Conflict::Switched
1970 );
1971 assert_eq!(subject.role(), Role::Controlled);
1972
1973 let mut subject = agent(false, 100, &[address(BOB)]);
1975 assert_eq!(
1976 subject.resolve_conflict(Some(controlled(100))),
1977 Conflict::Switched
1978 );
1979 assert_eq!(subject.role(), Role::Controlling);
1980
1981 let mut subject = agent(false, 100, &[address(BOB)]);
1983 assert_eq!(
1984 subject.resolve_conflict(Some(controlled(200))),
1985 Conflict::Reject
1986 );
1987 assert_eq!(subject.role(), Role::Controlled);
1988
1989 let mut subject = agent(true, 100, &[address(BOB)]);
1991 assert_eq!(
1992 subject.resolve_conflict(Some(controlled(200))),
1993 Conflict::None
1994 );
1995 assert_eq!(subject.role(), Role::Controlling);
1996
1997 let mut subject = agent(false, 100, &[address(BOB)]);
1999 assert_eq!(
2000 subject.resolve_conflict(Some(controlling(200))),
2001 Conflict::None
2002 );
2003 assert_eq!(subject.role(), Role::Controlled);
2004
2005 let mut subject = agent(true, 100, &[address(BOB)]);
2007 assert_eq!(subject.resolve_conflict(None), Conflict::None);
2008 assert_eq!(subject.role(), Role::Controlling);
2009 }
2010
2011 #[test]
2013 fn a_rejected_role_conflict_answers_487_and_not_a_success() {
2014 let mut subject = agent(true, 100, &[address(BOB)]);
2015 let outputs = deliver(
2016 &mut subject,
2017 address(BOB),
2018 peer_check(RoleAttribute::Controlling {
2019 tiebreaker: 100,
2020 nominate: false,
2021 }),
2022 );
2023 let answers = sent(&outputs);
2024 assert_eq!(answers.len(), 1);
2025 assert_eq!(answers[0].class(), Class::Error);
2026 assert_eq!(answers[0].error_code(), Some(stun::ROLE_CONFLICT));
2027 assert_eq!(subject.role(), Role::Controlling);
2028 }
2029
2030 #[test]
2033 fn a_switched_role_still_answers_the_check_that_caused_it() {
2034 let mut subject = agent(true, 100, &[address(BOB)]);
2035 let outputs = deliver(
2036 &mut subject,
2037 address(BOB),
2038 peer_check(RoleAttribute::Controlling {
2039 tiebreaker: 200,
2040 nominate: false,
2041 }),
2042 );
2043 let answers = sent(&outputs);
2044 assert_eq!(answers.len(), 1);
2045 assert_eq!(answers[0].class(), Class::Success);
2046 assert_eq!(subject.role(), Role::Controlled);
2047 }
2048
2049 #[test]
2053 fn a_487_switches_the_role_changes_the_tiebreaker_and_re_runs_the_check() {
2054 let mut subject = agent(true, 100, &[address(BOB)]);
2055 let check = subject.handle(Input::TimerFired(Timer::Ta));
2056 let outgoing = requests(&check);
2057 assert_eq!(outgoing.len(), 1);
2058 let transaction = outgoing[0].transaction();
2059 let before = subject.checklists().checklists()[0].pairs()[0].priority;
2060 let pair = subject.checklists().checklists()[0].pairs()[0].id;
2061 assert_eq!(
2062 subject.checklists().pair(pair).unwrap().state,
2063 PairState::InProgress
2064 );
2065
2066 let rejection = stun::role_conflict(transaction, &peer()).unwrap();
2067 subject.handle(Input::Datagram {
2068 from: address(BOB),
2069 on: LocalBase(0),
2070 bytes: rejection,
2071 });
2072
2073 assert_eq!(subject.role(), Role::Controlled);
2074 assert_ne!(subject.tiebreaker(), 100);
2075 assert_ne!(
2076 subject.checklists().checklists()[0].pairs()[0].priority,
2077 before,
2078 "a role switch swaps G and D, so every pair priority moves"
2079 );
2080 assert_eq!(
2081 subject.checklists().pair(pair).unwrap().state,
2082 PairState::Waiting
2083 );
2084 assert!(subject.checklists().checklists()[0].is_triggered(pair));
2085
2086 let rerun = subject.handle(Input::TimerFired(Timer::Ta));
2088 let rerun = requests(&rerun);
2089 assert_eq!(rerun.len(), 1);
2090 assert!(matches!(
2091 rerun[0].role(),
2092 Some(RoleAttribute::Controlled { .. })
2093 ));
2094 }
2095
2096 #[test]
2102 fn a_check_carries_the_peer_reflexive_priority_not_the_candidates_own() {
2103 let mut subject = agent(true, 100, &[address(BOB)]);
2104 let outputs = subject.handle(Input::TimerFired(Timer::Ta));
2105 let check = &requests(&outputs)[0];
2106 let candidate = subject.local_candidates()[0];
2107 assert_eq!(candidate.priority.get(), 2_130_706_431);
2108 assert_eq!(check.priority(), Some(candidate.check_priority()));
2109 assert_eq!(check.priority().unwrap().get(), 1_862_270_975);
2110 assert_ne!(check.priority(), Some(candidate.priority));
2111 }
2112
2113 #[test]
2118 fn a_check_from_an_unknown_address_teaches_a_remote_candidate() {
2119 let mut subject = agent(true, 100, &[address(BOB)]);
2120 assert_eq!(subject.remote_candidates().len(), 1);
2121 let behind_a_nat = address("198.51.100.7:41234");
2122 deliver(
2123 &mut subject,
2124 behind_a_nat,
2125 peer_check(RoleAttribute::Controlled { tiebreaker: 1 }),
2126 );
2127 let learned = subject
2128 .remote_candidates()
2129 .iter()
2130 .find(|candidate| candidate.address == behind_a_nat)
2131 .expect("§7.3.1.3 learns the source of an unmatched check");
2132 assert_eq!(learned.kind, CandidateType::PeerReflexive);
2133 assert_eq!(learned.priority.get(), 1_862_270_975);
2134 assert_eq!(learned.component, ComponentId::RTP);
2135 }
2136
2137 #[test]
2141 fn a_mapped_address_we_do_not_have_teaches_a_local_candidate() {
2142 let mut subject = agent(true, 100, &[address(BOB)]);
2143 let outputs = subject.handle(Input::TimerFired(Timer::Ta));
2144 let transaction = requests(&outputs)[0].transaction();
2145 let reflexive = address("198.51.100.4:33445");
2146 let response = stun::check_success(transaction, &peer(), reflexive).unwrap();
2147 deliver(&mut subject, address(BOB), response);
2148
2149 let learned = subject
2150 .local_candidates()
2151 .iter()
2152 .find(|candidate| candidate.gathered.address == reflexive)
2153 .expect("§7.2.5.3.1 learns the mapped address");
2154 assert_eq!(learned.gathered.kind, CandidateType::PeerReflexive);
2155 assert_eq!(learned.priority.get(), 1_862_270_975);
2156 assert_eq!(learned.gathered.base_address, address(ALICE));
2157 }
2158
2159 #[test]
2164 fn a_triggered_check_preempts_the_highest_priority_waiting_pair() {
2165 let low = address("198.51.100.8:40000");
2166 let mut subject = agent(true, 100, &[address(BOB), low]);
2167 let surprise = address("198.51.100.9:41000");
2169 deliver(
2170 &mut subject,
2171 surprise,
2172 peer_check(RoleAttribute::Controlled { tiebreaker: 1 }),
2173 );
2174
2175 let outputs = subject.handle(Input::TimerFired(Timer::Ta));
2176 let addressed: Vec<SocketAddr> = outputs
2177 .iter()
2178 .filter_map(|output| match output {
2179 Output::Send { to, bytes, .. } if Message::decode(bytes).is_ok() => Some(*to),
2180 _ => None,
2181 })
2182 .collect();
2183 assert_eq!(
2184 addressed,
2185 vec![surprise],
2186 "the triggered check goes first, ahead of every Waiting pair"
2187 );
2188 }
2189
2190 #[test]
2193 fn the_timers_and_the_pair_limit_are_the_configured_ones() {
2194 let config = Config {
2195 timers: Timers {
2196 ta: Duration::from_millis(20),
2197 ..Timers::default()
2198 },
2199 pair_limit: 3,
2200 };
2201 let remotes: Vec<SocketAddr> = (1..=8)
2202 .map(|n| address(&format!("198.51.100.{n}:5000")))
2203 .collect();
2204 let mut subject = Agent::new(config, true, credentials("aaaa"), 100);
2205 subject.handle(Input::LocalCandidate(host(address(ALICE))));
2206 subject.handle(Input::RemoteDescription {
2207 credentials: credentials("bbbb"),
2208 candidates: remotes
2209 .iter()
2210 .enumerate()
2211 .map(|(index, remote)| host_line(*remote, &(index + 1).to_string()))
2212 .collect(),
2213 lite: false,
2214 });
2215 let started = subject.handle(Input::GatheringDone);
2216
2217 assert_eq!(subject.checklists().total_pairs(), 3);
2218 assert!(started.contains(&Output::SetTimer {
2219 timer: Timer::Ta,
2220 after: Duration::from_millis(20),
2221 }));
2222
2223 let tick = subject.handle(Input::TimerFired(Timer::Ta));
2224 assert!(tick.contains(&Output::SetTimer {
2225 timer: Timer::Ta,
2226 after: Duration::from_millis(20),
2227 }));
2228 }
2229
2230 #[test]
2236 fn a_response_from_the_wrong_address_fails_the_pair() {
2237 let mut subject = agent(true, 100, &[address(BOB)]);
2238 let outputs = subject.handle(Input::TimerFired(Timer::Ta));
2239 let transaction = requests(&outputs)[0].transaction();
2240 let pair = subject.checklists().checklists()[0].pairs()[0].id;
2241
2242 let response = stun::check_success(transaction, &peer(), address(ALICE)).unwrap();
2243 deliver(&mut subject, address("198.51.100.66:5000"), response);
2244
2245 assert_eq!(
2246 subject.checklists().pair(pair).unwrap().state,
2247 PairState::Failed
2248 );
2249 assert!(subject.checklists().checklists()[0].valid().is_empty());
2250 }
2251
2252 #[test]
2257 fn a_response_with_the_wrong_credential_moves_no_state() {
2258 let mut subject = agent(true, 100, &[address(BOB)]);
2259 let outputs = subject.handle(Input::TimerFired(Timer::Ta));
2260 let transaction = requests(&outputs)[0].transaction();
2261 let pair = subject.checklists().checklists()[0].pairs()[0].id;
2262
2263 let forged = Peering::new(credentials("zzzz"), credentials("aaaa"));
2266 let response = stun::check_success(transaction, &forged, address(ALICE)).unwrap();
2267 let answered = deliver(&mut subject, address(BOB), response);
2268
2269 assert!(answered.is_empty());
2270 assert_eq!(
2271 subject.checklists().pair(pair).unwrap().state,
2272 PairState::InProgress
2273 );
2274 }
2275
2276 #[test]
2281 fn the_rto_is_recomputed_for_every_transaction() {
2282 let remotes: Vec<SocketAddr> = (1..=5)
2283 .map(|n| address(&format!("198.51.100.{n}:5000")))
2284 .collect();
2285 let mut subject = agent(false, 100, &remotes);
2288
2289 let first = subject.handle(Input::TimerFired(Timer::Ta));
2290 let first_rto = retransmit_after(&first).expect("a check arms its retransmission timer");
2291 let transaction = requests(&first)[0].transaction();
2292 let destination = match first.first() {
2293 Some(Output::Send { to, .. }) => *to,
2294 other => panic!("expected a check, got {other:?}"),
2295 };
2296
2297 let response = stun::check_success(transaction, &peer(), address(ALICE)).unwrap();
2298 deliver(&mut subject, destination, response);
2299
2300 let second = subject.handle(Input::TimerFired(Timer::Ta));
2301 let second_rto = retransmit_after(&second).expect("the next check arms its own");
2302 assert!(
2303 second_rto < first_rto,
2304 "one fewer outstanding check must shorten the RTO: {second_rto:?} vs {first_rto:?}"
2305 );
2306 }
2307
2308 #[test]
2311 fn a_check_is_retransmitted_rc_times_before_the_pair_fails() {
2312 let mut subject = agent(true, 100, &[address(BOB)]);
2313 let first = subject.handle(Input::TimerFired(Timer::Ta));
2314 let pair = subject.checklists().checklists()[0].pairs()[0].id;
2315 let mut interval = retransmit_after(&first).unwrap();
2316
2317 for _ in 1..Config::default().timers.rc {
2319 let outputs = subject.handle(Input::TimerFired(Timer::Retransmit(pair)));
2320 assert_eq!(requests(&outputs).len(), 1, "a retransmission is a resend");
2321 let next = retransmit_after(&outputs).unwrap();
2322 assert_eq!(next, interval * 2);
2323 interval = next;
2324 assert_eq!(
2325 subject.checklists().pair(pair).unwrap().state,
2326 PairState::InProgress
2327 );
2328 }
2329
2330 let last = subject.handle(Input::TimerFired(Timer::Retransmit(pair)));
2332 assert!(requests(&last).is_empty());
2333 let timers = Config::default().timers;
2334 assert_eq!(
2335 retransmit_after(&last),
2336 Some(timers.final_wait(retransmit_after(&first).unwrap()))
2337 );
2338 assert_eq!(
2339 subject.checklists().pair(pair).unwrap().state,
2340 PairState::InProgress
2341 );
2342
2343 let done = subject.handle(Input::TimerFired(Timer::Retransmit(pair)));
2345 assert_eq!(
2346 subject.checklists().pair(pair).unwrap().state,
2347 PairState::Failed
2348 );
2349 assert!(
2350 done.iter()
2351 .any(|output| matches!(output, Output::Failed { .. })),
2352 "the only pair failed, so the component failed"
2353 );
2354 }
2355
2356 #[test]
2361 fn two_agents_converge_on_a_selected_pair() {
2362 let (alice_address, bob_address) = (address(ALICE), address(BOB));
2363 let (mut alice, mut bob, mut left, mut right) = two_agents((true, false), (900, 100));
2364 assert_eq!(alice.role(), Role::Controlling);
2365 assert_eq!(bob.role(), Role::Controlled);
2366
2367 exchange(&mut alice, &mut bob, &mut left, &mut right, 6);
2368
2369 assert_eq!(
2370 alice.selected(ComponentId::RTP),
2371 Some((LocalBase(0), bob_address))
2372 );
2373 assert_eq!(
2374 bob.selected(ComponentId::RTP),
2375 Some((LocalBase(0), alice_address))
2376 );
2377 assert_eq!(
2378 alice.checklists().checklists()[0].state(),
2379 ChecklistState::Completed
2380 );
2381 assert!(!left.armed && !right.armed);
2386 }
2387
2388 #[test]
2393 fn a_check_arriving_after_the_checklist_concluded_still_arms_ta() {
2394 let (mut alice, mut bob, mut left, mut right) = two_agents((true, false), (900, 100));
2395 exchange(&mut alice, &mut bob, &mut left, &mut right, 12);
2397 assert_eq!(
2398 bob.checklists().checklists()[0].state(),
2399 ChecklistState::Completed
2400 );
2401 assert!(!right.armed, "a concluded checklist stops pacing");
2402
2403 let surprise = address("198.51.100.77:41000");
2405 let check = stun::connectivity_check(
2406 stun::new_transaction_id(),
2407 &Peering::new(credentials("aaaa"), credentials("bbbb")),
2408 Priority::new(1_862_270_975).unwrap(),
2409 RoleAttribute::Controlling {
2410 tiebreaker: 900,
2411 nominate: false,
2412 },
2413 )
2414 .unwrap();
2415 let outputs = bob.handle(Input::Datagram {
2416 from: surprise,
2417 on: LocalBase(0),
2418 bytes: check,
2419 });
2420 right.absorb(&outputs);
2421 assert!(
2422 right.armed,
2423 "the triggered check §7.3.1.4 just enqueued needs a Ta tick to leave"
2424 );
2425
2426 let tick = right.tick(&mut bob);
2427 let addressed: Vec<SocketAddr> = tick
2428 .iter()
2429 .filter_map(|output| match output {
2430 Output::Send { to, bytes, .. } if Message::decode(bytes).is_ok() => Some(*to),
2431 _ => None,
2432 })
2433 .collect();
2434 assert_eq!(addressed, vec![surprise]);
2435 }
2436
2437 #[test]
2440 fn the_controlling_agent_nominates_a_component_exactly_once() {
2441 let (alice_address, bob_address) = (address(ALICE), address(BOB));
2442 let mut alice = Agent::new(Config::default(), true, credentials("aaaa"), 900);
2443 let mut bob = Agent::new(Config::default(), false, credentials("bbbb"), 100);
2444 alice.handle(Input::LocalCandidate(host(alice_address)));
2445 bob.handle(Input::LocalCandidate(host(bob_address)));
2446 alice.handle(Input::RemoteDescription {
2447 credentials: credentials("bbbb"),
2448 candidates: vec![host_line(bob_address, "1")],
2449 lite: false,
2450 });
2451 bob.handle(Input::RemoteDescription {
2452 credentials: credentials("aaaa"),
2453 candidates: vec![host_line(alice_address, "1")],
2454 lite: false,
2455 });
2456 alice.handle(Input::GatheringDone);
2457 bob.handle(Input::GatheringDone);
2458
2459 let mut nominations = 0usize;
2460 for _ in 0..10 {
2461 let outputs = alice.handle(Input::TimerFired(Timer::Ta));
2462 for message in requests(&outputs) {
2463 if message.use_candidate() {
2464 nominations += 1;
2465 }
2466 }
2467 for output in outputs {
2468 if let Output::Send { bytes, .. } = output {
2469 for answer in bob.handle(Input::Datagram {
2470 from: alice_address,
2471 on: LocalBase(0),
2472 bytes,
2473 }) {
2474 if let Output::Send { bytes, .. } = answer {
2475 alice.handle(Input::Datagram {
2476 from: bob_address,
2477 on: LocalBase(0),
2478 bytes,
2479 });
2480 }
2481 }
2482 }
2483 }
2484 }
2485 assert_eq!(nominations, 1, "regular nomination nominates once");
2486 }
2487
2488 #[test]
2492 fn a_controlled_agent_never_sends_use_candidate() {
2493 let mut subject = agent(false, 100, &[address(BOB)]);
2494 let mut seen = 0usize;
2495 for _ in 0..6 {
2496 let outputs = subject.handle(Input::TimerFired(Timer::Ta));
2497 for message in requests(&outputs) {
2498 assert!(!message.use_candidate());
2499 assert!(matches!(
2500 message.role(),
2501 Some(RoleAttribute::Controlled { .. })
2502 ));
2503 seen += 1;
2504 }
2505 for message in requests(&outputs) {
2507 let response =
2508 stun::check_success(message.transaction(), &peer(), address(ALICE)).unwrap();
2509 deliver(&mut subject, address(BOB), response);
2510 }
2511 }
2512 assert!(seen > 0, "the controlled agent still sends checks");
2513 }
2514
2515 #[test]
2523 fn a_peer_that_nominates_twice_selects_the_highest_priority_nominated_pair() {
2524 let low = address("198.51.100.3:6000");
2525 let high = address("198.51.100.2:6000");
2526 let mut subject = Agent::new(Config::default(), false, credentials("aaaa"), 100);
2527 let mut driver = Driver::default();
2528 driver.absorb(&subject.handle(Input::LocalCandidate(host(address(ALICE)))));
2529 driver.absorb(&subject.handle(Input::RemoteDescription {
2530 credentials: credentials("bbbb"),
2531 candidates: vec![
2532 Candidate::parse(&format!(
2533 "1 1 UDP 1000 {} {} typ host",
2534 low.ip(),
2535 low.port()
2536 ))
2537 .unwrap(),
2538 Candidate::parse(&format!(
2539 "2 1 UDP 2130706431 {} {} typ host",
2540 high.ip(),
2541 high.port()
2542 ))
2543 .unwrap(),
2544 ],
2545 lite: false,
2546 }));
2547 driver.absorb(&subject.handle(Input::GatheringDone));
2548
2549 for remote in [low, high] {
2551 let nominating = stun::connectivity_check(
2552 stun::new_transaction_id(),
2553 &peer(),
2554 Priority::new(1_862_270_975).unwrap(),
2555 RoleAttribute::Controlling {
2556 tiebreaker: 999,
2557 nominate: true,
2558 },
2559 )
2560 .unwrap();
2561 driver.absorb(&deliver(&mut subject, remote, nominating));
2562
2563 let mut rounds = 0;
2564 while driver.armed && rounds < 10 {
2565 rounds += 1;
2566 let outputs = driver.tick(&mut subject);
2567 let destinations: Vec<(SocketAddr, TransactionId)> = outputs
2568 .iter()
2569 .filter_map(|output| match output {
2570 Output::Send { to, bytes, .. } => Message::decode(bytes)
2571 .ok()
2572 .filter(|message| message.class() == Class::Request)
2573 .map(|message| (*to, message.transaction())),
2574 _ => None,
2575 })
2576 .collect();
2577 for (to, transaction) in destinations {
2578 let response =
2579 stun::check_success(transaction, &peer(), address(ALICE)).unwrap();
2580 driver.absorb(&deliver(&mut subject, to, response));
2581 }
2582 }
2583 }
2584
2585 assert_eq!(
2586 subject.selected(ComponentId::RTP),
2587 Some((LocalBase(0), high)),
2588 "§8.1.1: use the pair with the highest priority among the nominated ones"
2589 );
2590 }
2591
2592 #[test]
2600 fn a_flood_of_checks_from_new_addresses_cannot_grow_the_set_past_the_limit() {
2601 let config = Config {
2602 pair_limit: 4,
2603 ..Config::default()
2604 };
2605 let mut subject = Agent::new(config, false, credentials("aaaa"), 100);
2606 let mut driver = Driver::default();
2607 driver.absorb(&subject.handle(Input::LocalCandidate(host(address(ALICE)))));
2608 driver.absorb(&subject.handle(Input::RemoteDescription {
2609 credentials: credentials("bbbb"),
2610 candidates: vec![host_line(address(BOB), "1")],
2611 lite: false,
2612 }));
2613 driver.absorb(&subject.handle(Input::GatheringDone));
2614
2615 let mut answered = 0usize;
2616 for n in 0..200u32 {
2617 let source = address(&format!("198.51.100.{}:{}", n % 200 + 1, 40000 + n));
2618 let outputs = deliver(
2619 &mut subject,
2620 source,
2621 peer_check(RoleAttribute::Controlling {
2622 tiebreaker: 999,
2623 nominate: false,
2624 }),
2625 );
2626 driver.absorb(&outputs);
2627 answered += sent(&outputs).len();
2628 }
2629
2630 assert_eq!(
2631 answered, 200,
2632 "§7.3 still answers every authenticated check — a 64-byte response to an 88-byte \
2633 request is not an amplifier"
2634 );
2635 assert!(
2636 subject.checklists().total_pairs() <= 4,
2637 "the checklist set grew to {} against a configured limit of 4",
2638 subject.checklists().total_pairs()
2639 );
2640 assert!(
2641 subject.remote_candidates().len() <= 5,
2642 "the remote candidate table grew to {}",
2643 subject.remote_candidates().len()
2644 );
2645
2646 let mut destinations: Vec<SocketAddr> = Vec::new();
2648 let mut rounds = 0;
2649 while driver.armed && rounds < 200 {
2650 rounds += 1;
2651 for output in driver.tick(&mut subject) {
2652 if let Output::Send { to, bytes, .. } = output
2653 && Message::decode(&bytes).is_ok_and(|m| m.class() == Class::Request)
2654 && !destinations.contains(&to)
2655 {
2656 destinations.push(to);
2657 }
2658 }
2659 }
2660 assert!(
2661 destinations.len() <= 4,
2662 "checks went to {} distinct addresses against a limit of 4",
2663 destinations.len()
2664 );
2665 }
2666
2667 fn remote_address_of(subject: &Agent, pair: PairId) -> Option<SocketAddr> {
2670 let remote = subject.checklists().pair(pair)?.remote;
2671 find_remote(subject.remote_candidates(), remote).map(|candidate| candidate.address)
2672 }
2673
2674 fn three_remote_agent() -> (Agent, Driver, Vec<SocketAddr>) {
2675 let remotes: Vec<SocketAddr> = (1..=3)
2676 .map(|n| address(&format!("198.51.100.{n}:6000")))
2677 .collect();
2678 let mut subject = Agent::new(Config::default(), true, credentials("aaaa"), 100);
2679 let mut driver = Driver::default();
2680 driver.absorb(&subject.handle(Input::LocalCandidate(host(address(ALICE)))));
2681 driver.absorb(
2682 &subject.handle(Input::RemoteDescription {
2683 credentials: credentials("bbbb"),
2684 candidates: remotes
2685 .iter()
2686 .enumerate()
2687 .map(|(index, remote)| host_line(*remote, &(index + 1).to_string()))
2688 .collect(),
2689 lite: false,
2690 }),
2691 );
2692 driver.absorb(&subject.handle(Input::GatheringDone));
2693 (subject, driver, remotes)
2694 }
2695
2696 #[test]
2702 fn a_second_description_adds_candidates_without_re_pointing_the_live_pairs() {
2703 let (mut subject, mut driver, remotes) = three_remote_agent();
2704 let before: Vec<(PairId, SocketAddr)> = subject.checklists().checklists()[0]
2705 .pairs()
2706 .iter()
2707 .map(|pair| (pair.id, remote_address_of(&subject, pair.id).unwrap()))
2708 .collect();
2709 assert_eq!(before.len(), 3);
2710
2711 let fresh = address("203.0.113.99:6000");
2712 driver.absorb(&subject.handle(Input::RemoteDescription {
2713 credentials: credentials("bbbb"),
2714 candidates: vec![host_line(fresh, "9")],
2715 lite: false,
2716 }));
2717
2718 for (pair, was) in &before {
2719 assert_eq!(
2720 remote_address_of(&subject, *pair),
2721 Some(*was),
2722 "a re-offer must not re-point a pair that is already being checked"
2723 );
2724 }
2725 assert!(
2726 subject
2727 .remote_candidates()
2728 .iter()
2729 .any(|candidate| candidate.address == fresh),
2730 "the candidate the second description brought is added"
2731 );
2732 for remote in &remotes {
2733 assert!(
2734 subject
2735 .remote_candidates()
2736 .iter()
2737 .any(|candidate| candidate.address == *remote),
2738 "a candidate the second description omitted is not dropped underneath its pair"
2739 );
2740 }
2741
2742 assert!(driver.armed);
2744 let destinations: Vec<SocketAddr> = driver
2745 .tick(&mut subject)
2746 .iter()
2747 .filter_map(|output| match output {
2748 Output::Send { to, bytes, .. } if Message::decode(bytes).is_ok() => Some(*to),
2749 _ => None,
2750 })
2751 .collect();
2752 assert_eq!(
2753 destinations.len(),
2754 1,
2755 "a re-offer must not silence the agent"
2756 );
2757 }
2758
2759 #[test]
2762 fn an_ice_restart_rebuilds_the_checklists_and_keeps_checking() {
2763 let (mut subject, mut driver, _) = three_remote_agent();
2764 let before: Vec<PairId> = subject.checklists().checklists()[0]
2765 .pairs()
2766 .iter()
2767 .map(|pair| pair.id)
2768 .collect();
2769
2770 let fresh = address("203.0.113.99:6000");
2771 driver.absorb(&subject.handle(Input::RemoteDescription {
2772 credentials: credentials("cccc"),
2773 candidates: vec![host_line(fresh, "1")],
2774 lite: false,
2775 }));
2776
2777 assert_eq!(subject.remote_candidates().len(), 1);
2778 assert_eq!(subject.checklists().total_pairs(), 1);
2779 for pair in before {
2780 assert!(
2781 subject.checklists().pair(pair).is_none(),
2782 "a restart is a new ICE session, so none of the old pairs survive it"
2783 );
2784 }
2785 assert!(driver.armed);
2786 let destinations: Vec<SocketAddr> = driver
2787 .tick(&mut subject)
2788 .iter()
2789 .filter_map(|output| match output {
2790 Output::Send { to, bytes, .. } if Message::decode(bytes).is_ok() => Some(*to),
2791 _ => None,
2792 })
2793 .collect();
2794 assert_eq!(destinations, vec![fresh]);
2795 }
2796
2797 #[test]
2805 fn two_agents_that_both_start_controlling_converge_on_one_role() {
2806 let (mut alice, mut bob, mut left, mut right) = both_controlling(0x1234_5678_9abc_def0);
2807 assert_eq!(alice.role(), Role::Controlling);
2808 assert_eq!(bob.role(), Role::Controlling);
2809
2810 exchange(&mut alice, &mut bob, &mut left, &mut right, 6);
2811
2812 assert_ne!(
2813 alice.role(),
2814 bob.role(),
2815 "two controlling agents never converge: neither accepts the other's nomination"
2816 );
2817 assert!(alice.role().is_controlling() || bob.role().is_controlling());
2818 }
2819}