1#![deny(unreachable_pub, unsafe_code)]
2
3use core::fmt;
6use rand::distr::{Alphanumeric, SampleString};
7use sdp_types::{IceCandidate, UntaggedAddress};
8use slotmap::{SlotMap, new_key_type};
9use std::{
10 cmp::{max, min},
11 collections::VecDeque,
12 hash::{DefaultHasher, Hash, Hasher},
13 mem::take,
14 net::{IpAddr, SocketAddr},
15 time::{Duration, Instant},
16};
17use stun::{StunConfig, StunServerBinding};
18use stun_types::{
19 Class, Message, TransactionId,
20 attributes::{
21 ErrorCode, Fingerprint, IceControlled, IceControlling, Priority, UseCandidate,
22 XorMappedAddress,
23 },
24};
25
26mod stun;
27
28pub struct ReceivedPkt<D = Vec<u8>> {
30 pub data: D,
32 pub source: SocketAddr,
34 pub destination: SocketAddr,
36 pub component: Component,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42#[repr(u8)]
43pub enum Component {
44 Rtp = 1,
46 Rtcp = 2,
48}
49
50#[derive(Debug)]
52pub enum IceEvent {
53 GatheringStateChanged {
54 old: IceGatheringState,
55 new: IceGatheringState,
56 },
57 ConnectionStateChanged {
58 old: IceConnectionState,
59 new: IceConnectionState,
60 },
61 DiscoveredAddr {
62 component: Component,
63 target: SocketAddr,
64 },
65 SendData {
66 component: Component,
67 data: Vec<u8>,
68 source: Option<IpAddr>,
69 target: SocketAddr,
70 },
71}
72
73pub struct IceAgent {
75 stun_config: StunConfig,
76
77 stun_server: Vec<StunServerBinding>,
78
79 local_credentials: IceCredentials,
80 remote_credentials: Option<IceCredentials>,
81
82 local_candidates: SlotMap<LocalCandidateId, Candidate>,
83 remote_candidates: SlotMap<RemoteCandidateId, Candidate>,
84
85 pairs: Vec<CandidatePair>,
86 triggered_check_queue: VecDeque<(LocalCandidateId, RemoteCandidateId)>,
87
88 rtcp_mux: bool,
89 is_controlling: bool,
90 control_tie_breaker: u64,
91 max_pairs: usize,
92
93 gathering_state: IceGatheringState,
94 connection_state: IceConnectionState,
95
96 last_ta_trigger: Option<Instant>,
97
98 backlog: Vec<(Instant, ReceivedPkt<Message>)>,
101
102 events: VecDeque<IceEvent>,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
108pub enum IceGatheringState {
109 New,
111 Gathering,
113 Complete,
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
120pub enum IceConnectionState {
122 Failed,
124
125 Disconnected,
130
131 New,
133 Checking,
135 Connected,
137}
138
139new_key_type!(
140 struct LocalCandidateId;
141 struct RemoteCandidateId;
142);
143
144#[derive(Debug, PartialEq, Clone, Copy, Hash)]
145enum CandidateKind {
146 Host = 126,
147 PeerReflexive = 110,
148 ServerReflexive = 100,
149 }
151
152#[derive(Debug)]
153struct Candidate {
154 addr: SocketAddr,
155 kind: CandidateKind,
157 priority: u32,
158 foundation: String,
159
160 component: Component,
161
162 base: SocketAddr,
167}
168
169#[derive(Debug)]
170struct CandidatePair {
171 local: LocalCandidateId,
172 remote: RemoteCandidateId,
173 priority: u64,
174 state: CandidatePairState,
175 component: Component,
176
177 nomination: CandidatePairNomination,
178}
179
180#[derive(Debug, PartialEq, Eq)]
181enum CandidatePairNomination {
182 None,
184 Nominated,
186 Selected,
188}
189
190#[derive(Debug, Clone, PartialEq)]
191enum CandidatePairState {
192 Waiting,
194
195 InProgress {
197 transaction_id: TransactionId,
198 stun_request: Vec<u8>,
199 retransmit_at: Instant,
200 retransmits: u32,
201 source: IpAddr,
202 target: SocketAddr,
203 },
204
205 Succeeded,
207
208 Failed,
211}
212
213#[derive(Clone)]
217pub struct IceCredentials {
218 pub ufrag: String,
219 pub pwd: String,
220}
221
222impl IceCredentials {
223 pub fn random() -> Self {
224 let mut rng = rand::rng();
225
226 Self {
227 ufrag: Alphanumeric.sample_string(&mut rng, 8),
228 pwd: Alphanumeric.sample_string(&mut rng, 32),
229 }
230 }
231}
232
233impl IceAgent {
234 pub fn new_from_answer(
235 local_credentials: IceCredentials,
236 remote_credentials: IceCredentials,
237 is_controlling: bool,
238 rtcp_mux: bool,
239 ) -> Self {
240 IceAgent {
241 stun_config: StunConfig::new(),
242 stun_server: vec![],
243 local_credentials,
244 remote_credentials: Some(remote_credentials),
245 local_candidates: SlotMap::with_key(),
246 remote_candidates: SlotMap::with_key(),
247 pairs: Vec::new(),
248 triggered_check_queue: VecDeque::new(),
249 rtcp_mux,
250 is_controlling,
251 control_tie_breaker: rand::random(),
252 max_pairs: 100,
253 gathering_state: IceGatheringState::New,
254 connection_state: IceConnectionState::New,
255 last_ta_trigger: None,
256 backlog: vec![],
257 events: VecDeque::new(),
258 }
259 }
260
261 pub fn new_for_offer(
262 local_credentials: IceCredentials,
263 is_controlling: bool,
264 rtcp_mux: bool,
265 ) -> Self {
266 IceAgent {
267 stun_config: StunConfig::new(),
268 stun_server: vec![],
269 local_credentials,
270 remote_credentials: None,
271 local_candidates: SlotMap::with_key(),
272 remote_candidates: SlotMap::with_key(),
273 pairs: Vec::new(),
274 triggered_check_queue: VecDeque::new(),
275 rtcp_mux,
276 is_controlling,
277 control_tie_breaker: rand::random(),
278 max_pairs: 100,
279 gathering_state: IceGatheringState::New,
280 connection_state: IceConnectionState::New,
281 last_ta_trigger: None,
282 backlog: vec![],
283 events: VecDeque::new(),
284 }
285 }
286
287 pub fn set_remote_data(
289 &mut self,
290 credentials: IceCredentials,
291 candidates: &[IceCandidate],
292 rtcp_mux: bool,
293 ) {
294 self.rtcp_mux = rtcp_mux;
296
297 if rtcp_mux {
299 self.stun_server.retain(|s| s.component() == Component::Rtp);
300 self.local_candidates
301 .retain(|_, c| c.component == Component::Rtp);
302 }
303
304 self.remote_credentials = Some(credentials);
305
306 for candidate in candidates {
307 self.add_remote_candidate(candidate);
308 }
309
310 for (instant, pkt) in take(&mut self.backlog) {
311 self.receive_stun(instant, pkt);
312 }
313 }
314
315 pub fn credentials(&self) -> &IceCredentials {
317 &self.local_credentials
318 }
319
320 pub fn add_host_addr(&mut self, component: Component, addr: SocketAddr) {
323 if addr.ip().is_unspecified() {
324 return;
325 }
326
327 if let SocketAddr::V6(v6) = addr {
328 let ip = v6.ip();
329 if ip.to_ipv4().is_some() || ip.to_ipv4_mapped().is_some() {
330 return;
331 }
332 }
333
334 self.add_local_candidate(component, CandidateKind::Host, addr, addr, None);
335 }
336
337 pub fn add_stun_server(&mut self, server: SocketAddr) {
339 self.stun_server
341 .push(StunServerBinding::new(server, Component::Rtp));
342
343 if !self.rtcp_mux {
344 self.stun_server
345 .push(StunServerBinding::new(server, Component::Rtcp));
346 }
347 }
348
349 pub fn gathering_state(&self) -> IceGatheringState {
351 self.gathering_state
352 }
353
354 pub fn connection_state(&self) -> IceConnectionState {
356 self.connection_state
357 }
358
359 pub fn discovered_addr(&self, mut component: Component) -> Option<(SocketAddr, SocketAddr)> {
361 if self.rtcp_mux {
362 component = Component::Rtp;
363 }
364
365 self.pairs
366 .iter()
367 .find(|pair| {
368 pair.component == component
369 && pair.state == CandidatePairState::Succeeded
370 && pair.nomination == CandidatePairNomination::Selected
371 })
372 .map(|pair| {
373 (
374 self.local_candidates[pair.local].addr,
375 self.remote_candidates[pair.remote].addr,
376 )
377 })
378 }
379
380 fn add_local_candidate(
381 &mut self,
382 component: Component,
383 kind: CandidateKind,
384 base: SocketAddr,
385 addr: SocketAddr,
386 stun_server: Option<SocketAddr>,
387 ) {
388 let already_exists = self
390 .local_candidates
391 .values()
392 .any(|c| c.kind == kind && c.base == base && c.addr == addr);
393
394 if already_exists {
395 return;
397 }
398
399 log::debug!("add local candidate {component:?} {kind:?} {addr}");
400
401 let local_preference_offset = match kind {
404 CandidateKind::Host => (65535 / 4) * 3,
405 CandidateKind::PeerReflexive => (65535 / 4) * 2,
406 CandidateKind::ServerReflexive => 65535 / 4,
407 };
409
410 let local_preference = self
411 .local_candidates
412 .values()
413 .filter(|c| c.component == component && c.kind == kind)
414 .count() as u32
415 + local_preference_offset;
416
417 let kind_preference = (kind as u32) << 24;
418 let local_preference = local_preference << 8;
419 let priority = kind_preference + local_preference + (256 - component as u32);
420
421 self.local_candidates.insert(Candidate {
422 addr,
423 kind,
424 priority,
425 foundation: compute_foundation(kind, base.ip(), stun_server, "udp").to_string(),
426 component,
427 base,
428 });
429
430 self.form_pairs();
431 }
432
433 pub fn add_remote_candidate(&mut self, candidate: &IceCandidate) {
435 let kind = match candidate.typ.as_str() {
436 "host" => CandidateKind::Host,
437 "srflx" => CandidateKind::ServerReflexive,
438 _ => return,
439 };
440
441 if !candidate.transport.eq_ignore_ascii_case("udp") {
443 return;
444 }
445
446 let Ok(priority) = u32::try_from(candidate.priority) else {
447 log::warn!("Candidate has priority larger than u32::MAX");
448 return;
449 };
450
451 let component = match candidate.component {
452 1 => Component::Rtp,
453 2 if !self.rtcp_mux => Component::Rtcp,
455 _ => {
456 log::debug!(
457 "Discard remote candidate with unsupported component candidate:{candidate}"
458 );
459 return;
460 }
461 };
462
463 let ip = match candidate.address {
464 UntaggedAddress::Fqdn(..) => return,
465 UntaggedAddress::IpAddress(ip_addr) => ip_addr,
466 };
467
468 self.remote_candidates.insert(Candidate {
469 addr: SocketAddr::new(ip, candidate.port),
470 kind,
471 priority,
472 foundation: candidate.foundation.to_string(),
473 component,
474 base: SocketAddr::new(ip, candidate.port), });
476
477 self.form_pairs();
478 }
479
480 fn form_pairs(&mut self) {
481 for (local_id, local_candidate) in &self.local_candidates {
482 for (remote_id, remote_candidate) in &self.remote_candidates {
483 if remote_candidate.kind == CandidateKind::PeerReflexive {
485 continue;
486 }
487
488 if local_candidate.component != remote_candidate.component {
490 continue;
491 }
492
493 let already_exists = self
495 .pairs
496 .iter()
497 .any(|pair| pair.local == local_id && pair.remote == remote_id);
498
499 if already_exists {
500 continue;
501 }
502
503 match (local_candidate.addr.ip(), remote_candidate.addr.ip()) {
505 (IpAddr::V4(l), IpAddr::V4(r)) if l.is_link_local() == r.is_link_local() => {
506 }
508 (IpAddr::V6(l), IpAddr::V6(r))
510 if l.is_unicast_link_local() == r.is_unicast_link_local() =>
511 { }
512 _ => {
513 continue;
515 }
516 }
517
518 Self::add_candidate_pair(
519 local_id,
520 local_candidate,
521 remote_id,
522 remote_candidate,
523 self.is_controlling,
524 &mut self.pairs,
525 false,
526 );
527 }
528 }
529
530 self.pairs
531 .sort_unstable_by(|a, b| b.priority.cmp(&a.priority));
532
533 self.prune_pairs();
534 }
535
536 fn add_candidate_pair(
537 local_id: LocalCandidateId,
538 local_candidate: &Candidate,
539 remote_id: RemoteCandidateId,
540 remote_candidate: &Candidate,
541 is_controlling: bool,
542 pairs: &mut Vec<CandidatePair>,
543 received_use_candidate: bool,
544 ) {
545 if pairs
546 .iter()
547 .any(|p| p.local == local_id && p.remote == remote_id)
548 {
549 return;
551 }
552
553 let priority = pair_priority(local_candidate, remote_candidate, is_controlling);
554
555 log::debug!(
556 "add pair {}, priority: {priority}, component={:?}",
557 DisplayPair(local_candidate, remote_candidate),
558 local_candidate.component,
559 );
560
561 pairs.push(CandidatePair {
562 local: local_id,
563 remote: remote_id,
564 priority,
565 state: CandidatePairState::Waiting,
566 component: local_candidate.component,
567 nomination: if received_use_candidate {
568 CandidatePairNomination::Selected
569 } else {
570 CandidatePairNomination::None
571 },
572 });
573 pairs.sort_unstable_by(|a, b| b.priority.cmp(&a.priority));
574 }
575
576 fn recompute_pair_priorities(&mut self) {
577 for pair in &mut self.pairs {
578 pair.priority = pair_priority(
579 &self.local_candidates[pair.local],
580 &self.remote_candidates[pair.remote],
581 self.is_controlling,
582 );
583 }
584
585 self.pairs
586 .sort_unstable_by(|a, b| b.priority.cmp(&a.priority));
587 }
588
589 fn prune_pairs(&mut self) {
591 let mut to_prune = Vec::new();
592
593 for (index, pair) in self.pairs.iter().enumerate().rev() {
594 if (self.pairs.len() - to_prune.len()) <= self.max_pairs {
596 break;
597 }
598
599 if pair.nomination != CandidatePairNomination::None {
601 continue;
602 }
603
604 if matches!(
606 pair.state,
607 CandidatePairState::Succeeded | CandidatePairState::InProgress { .. }
608 ) {
609 continue;
610 }
611
612 to_prune.push(index);
613 }
614
615 for index in to_prune {
616 let pair = self.pairs.remove(index);
617
618 log::debug!(
619 "pruned pair {}",
620 DisplayPair(
621 &self.local_candidates[pair.local],
622 &self.remote_candidates[pair.remote]
623 )
624 );
625 }
626 }
627
628 pub fn receive(&mut self, now: Instant, pkt: ReceivedPkt) {
630 let mut stun_msg = match Message::parse(pkt.data) {
631 Ok(stun_msg) => stun_msg,
632 Err(e) => {
633 log::debug!("Failed to parse stun message {e}");
634 return;
635 }
636 };
637
638 let passed_fingerprint_check = stun_msg
639 .attribute::<Fingerprint>()
640 .is_some_and(|r| r.is_ok());
641
642 if !passed_fingerprint_check {
643 log::trace!(
644 "Incoming STUN {:?} failed fingerprint check, discarding",
645 stun_msg.class()
646 );
647 return;
648 }
649
650 let pkt = ReceivedPkt {
651 data: stun_msg,
652 source: pkt.source,
653 destination: pkt.destination,
654 component: pkt.component,
655 };
656
657 self.receive_stun(now, pkt);
658 }
659
660 fn receive_stun(&mut self, now: Instant, pkt: ReceivedPkt<Message>) {
661 match pkt.data.class() {
662 Class::Request => self.receive_stun_request(now, pkt),
663 Class::Indication => { }
664 Class::Success => self.receive_stun_success(now, pkt),
665 Class::Error => self.receive_stun_error(now, pkt),
666 }
667 }
668
669 fn receive_stun_success(&mut self, now: Instant, mut pkt: ReceivedPkt<Message>) {
670 for stun_server_binding in &mut self.stun_server {
672 if !stun_server_binding.wants_stun_response(pkt.data.transaction_id()) {
673 continue;
674 }
675
676 let Some(addr) =
677 stun_server_binding.receive_stun_response(now, &self.stun_config, pkt.data)
678 else {
679 return;
681 };
682
683 let component = stun_server_binding.component();
684 let stun_server = stun_server_binding.server();
685 self.add_local_candidate(
686 component,
687 CandidateKind::ServerReflexive,
688 pkt.destination,
689 addr,
690 Some(stun_server),
691 );
692
693 return;
694 }
695
696 let Some(remote_credentials) = &self.remote_credentials else {
698 if self.backlog.len() < 128 {
699 self.backlog.push((now, pkt));
700 }
701 return;
702 };
703
704 if !stun::verify_integrity(&self.local_credentials, remote_credentials, &mut pkt.data) {
705 log::debug!("Incoming stun success failed the integrity check, discarding");
706 return;
707 }
708
709 let Some(pair) = self
715 .pairs
716 .iter_mut()
717 .find(|p| {
718 matches!(p.state, CandidatePairState::InProgress { transaction_id, .. } if pkt.data.transaction_id() == transaction_id)
719 }) else {
720 log::debug!("Failed to find transaction for STUN success, discarding");
721 return;
722 };
723
724 let CandidatePairState::InProgress { source, target, .. } = &pair.state else {
725 unreachable!()
726 };
727
728 if pkt.source == *target && pkt.destination.ip() == *source {
729 log::debug!(
730 "got success response for pair {} nomination={:?}",
731 DisplayPair(
732 &self.local_candidates[pair.local],
733 &self.remote_candidates[pair.remote],
734 ),
735 pair.nomination,
736 );
737
738 if pair.nomination == CandidatePairNomination::Nominated {
740 pair.nomination = CandidatePairNomination::Selected;
741
742 let local_candidate = &self.local_candidates[pair.local];
743 let remote_candidate = &self.remote_candidates[pair.remote];
744
745 self.events.push_back(IceEvent::DiscoveredAddr {
746 component: local_candidate.component,
747 target: remote_candidate.addr,
748 });
749
750 self.triggered_check_queue.retain(|(local_id, _)| {
752 self.local_candidates[*local_id].component != local_candidate.component
753 });
754 }
755
756 pair.state = CandidatePairState::Succeeded;
757 } else {
758 log::debug!(
759 "got success response with invalid source address for pair {}",
760 DisplayPair(
761 &self.local_candidates[pair.local],
762 &self.remote_candidates[pair.remote]
763 )
764 );
765
766 pair.nomination = CandidatePairNomination::None;
772 pair.state = CandidatePairState::Failed;
773 }
774
775 if let Some(Ok(mapped_addr)) = pkt.data.attribute::<XorMappedAddress>() {
777 if mapped_addr.0 != self.local_candidates[pair.local].addr {
778 let component = pair.component;
779 self.add_local_candidate(
780 component,
781 CandidateKind::PeerReflexive,
782 pkt.destination,
783 mapped_addr.0,
784 None,
785 );
786 }
787 } else {
788 log::trace!("no (valid) XOR-MAPPED-ADDRESS attribute in STUN success response");
789 }
790 }
791
792 fn receive_stun_error(&mut self, now: Instant, mut pkt: ReceivedPkt<Message>) {
793 let Some(remote_credentials) = &self.remote_credentials else {
794 if self.backlog.len() < 128 {
795 self.backlog.push((now, pkt));
796 }
797 return;
798 };
799
800 if !stun::verify_integrity(&self.local_credentials, remote_credentials, &mut pkt.data) {
801 log::debug!("Incoming stun error response failed the integrity check, discarding");
802 return;
803 }
804
805 let Some(pair) = self
806 .pairs
807 .iter_mut()
808 .find(|p| {
809 matches!(p.state, CandidatePairState::InProgress { transaction_id, .. } if pkt.data.transaction_id() == transaction_id)
810 }) else {
811 log::debug!("Failed to find transaction for STUN error, discarding");
812 return;
813 };
814
815 let error_code = pkt.data.attribute::<ErrorCode>().and_then(|r| r.ok());
816
817 let display_pair = DisplayPair(
818 &self.local_candidates[pair.local],
819 &self.remote_candidates[pair.remote],
820 );
821
822 if let Some(error_code) = &error_code {
823 log::debug!(
824 "Candidate pair {display_pair} failed with code={}, reason={}",
825 error_code.number,
826 error_code.reason
827 );
828 } else {
829 log::debug!("Candidate pair {display_pair} failed without error code");
830 }
831
832 if error_code.is_some_and(|e| e.number == 487) {
837 if pkt.data.attribute::<IceControlled>().is_some() {
838 self.is_controlling = true;
839 } else if pkt.data.attribute::<IceControlling>().is_some() {
840 self.is_controlling = false;
841 }
842
843 pair.state = CandidatePairState::Waiting;
848 self.triggered_check_queue
849 .push_back((pair.local, pair.remote));
850
851 self.recompute_pair_priorities();
853 } else {
854 pair.state = CandidatePairState::Failed;
855 pair.nomination = CandidatePairNomination::None;
856 }
857 }
858
859 fn receive_stun_request(&mut self, now: Instant, mut pkt: ReceivedPkt<Message>) {
860 let Some(remote_credentials) = &self.remote_credentials else {
861 if self.backlog.len() < 128 {
862 self.backlog.push((now, pkt));
863 }
864 return;
865 };
866
867 if !stun::verify_integrity(&self.local_credentials, remote_credentials, &mut pkt.data) {
868 log::debug!("Incoming stun request failed the integrity check, discarding");
869 return;
870 }
871
872 let Some(Ok(priority)) = pkt.data.attribute::<Priority>() else {
873 log::debug!("Incoming stun request did not contain PRIORITY attribute");
874 return;
875 };
876
877 let use_candidate = pkt.data.attribute::<UseCandidate>().is_some();
878
879 if self.is_controlling {
881 if let Some(Ok(ice_controlling)) = pkt.data.attribute::<IceControlling>() {
882 if self.control_tie_breaker >= ice_controlling.0 {
883 let response = stun::make_role_error(
884 pkt.data.transaction_id(),
885 &self.local_credentials,
886 remote_credentials,
887 pkt.source,
888 true,
889 self.control_tie_breaker,
890 );
891
892 self.events.push_back(IceEvent::SendData {
893 component: pkt.component,
894 data: response,
895 source: Some(pkt.destination.ip()),
896 target: pkt.source,
897 });
898
899 return;
900 } else {
901 self.is_controlling = false;
902 self.recompute_pair_priorities();
903 }
904 }
905 } else if !self.is_controlling
906 && let Some(Ok(ice_controlled)) = pkt.data.attribute::<IceControlled>()
907 {
908 if self.control_tie_breaker >= ice_controlled.0 {
909 let response = stun::make_role_error(
910 pkt.data.transaction_id(),
911 &self.local_credentials,
912 remote_credentials,
913 pkt.source,
914 false,
915 self.control_tie_breaker,
916 );
917
918 self.events.push_back(IceEvent::SendData {
919 component: pkt.component,
920 data: response,
921 source: Some(pkt.destination.ip()),
922 target: pkt.source,
923 });
924 return;
925 } else {
926 self.is_controlling = true;
927 self.recompute_pair_priorities();
928 }
929 }
930
931 let local_id = match self
932 .local_candidates
933 .iter()
934 .find(|(_, c)| c.kind == CandidateKind::Host && c.addr == pkt.destination)
935 {
936 Some((id, _)) => id,
937 None => {
938 log::warn!(
939 "Failed to find matching local candidate for incoming STUN request ({})?",
940 pkt.destination
941 );
942 return;
943 }
944 };
945
946 let matching_remote_candidate = self.remote_candidates.iter().find(|(_, c)| {
947 c.addr == pkt.source
949 });
950
951 let remote_id = match matching_remote_candidate {
952 Some((remote, _)) => remote,
953 None => {
954 let peer_reflexive_id = self.remote_candidates.insert(Candidate {
956 addr: pkt.source,
957 kind: CandidateKind::PeerReflexive,
958 priority: priority.0,
959 foundation: "~".into(),
960 component: pkt.component,
961 base: pkt.source,
962 });
963
964 Self::add_candidate_pair(
966 local_id,
967 &self.local_candidates[local_id],
968 peer_reflexive_id,
969 &self.remote_candidates[peer_reflexive_id],
970 self.is_controlling,
971 &mut self.pairs,
972 false,
973 );
974
975 self.triggered_check_queue
976 .push_back((local_id, peer_reflexive_id));
977
978 peer_reflexive_id
979 }
980 };
981
982 let pair = if let Some(pair) = self
983 .pairs
984 .iter_mut()
985 .find(|p| p.local == local_id && p.remote == remote_id)
986 {
987 pair
988 } else {
989 log::debug!(
990 "Got STUN request for candidates without pair, local-candidate: {} remote-candidate: {}, adding new pair",
991 DisplayCandidate(&self.local_candidates[local_id]),
992 DisplayCandidate(&self.remote_candidates[remote_id])
993 );
994
995 Self::add_candidate_pair(
997 local_id,
998 &self.local_candidates[local_id],
999 remote_id,
1000 &self.remote_candidates[remote_id],
1001 self.is_controlling,
1002 &mut self.pairs,
1003 false,
1004 );
1005
1006 self.triggered_check_queue.push_back((local_id, remote_id));
1007
1008 self.pairs
1009 .iter_mut()
1010 .find(|p| p.local == local_id && p.remote == remote_id)
1011 .expect("just inserted pair")
1012 };
1013
1014 if use_candidate && !self.is_controlling {
1015 pair.nomination = CandidatePairNomination::Selected;
1016 }
1017
1018 log::trace!(
1019 "got connectivity check for pair {}",
1020 DisplayPair(
1021 &self.local_candidates[pair.local],
1022 &self.remote_candidates[pair.remote],
1023 )
1024 );
1025
1026 let stun_response = stun::make_success_response(
1027 pkt.data.transaction_id(),
1028 &self.local_credentials,
1029 pkt.source,
1030 );
1031
1032 self.events.push_back(IceEvent::SendData {
1033 component: pair.component,
1034 data: stun_response,
1035 source: Some(self.local_candidates[local_id].base.ip()),
1036 target: pkt.source,
1037 });
1038
1039 if use_candidate {
1041 self.poll_nomination();
1042 }
1043 }
1044
1045 pub fn poll(&mut self, now: Instant) {
1047 for stun_server_bindings in &mut self.stun_server {
1049 stun_server_bindings.poll(now, &self.stun_config, |event| self.events.push_back(event));
1050 }
1051
1052 self.poll_retransmit(now);
1054 self.poll_state();
1055 self.poll_nomination();
1056
1057 let Some(remote_credentials) = &self.remote_credentials else {
1059 return;
1060 };
1061
1062 if let Some(it) = self.last_ta_trigger
1064 && it + Duration::from_millis(50) > now
1065 {
1066 return;
1067 }
1068 self.last_ta_trigger = Some(now);
1069
1070 let pair = self
1076 .triggered_check_queue
1077 .pop_front()
1078 .and_then(|(local_id, remote_id)| {
1079 self.pairs
1080 .iter_mut()
1081 .find(|p| p.local == local_id && p.remote == remote_id)
1082 });
1083
1084 let pair = if let Some(pair) = pair {
1085 Some(pair)
1086 } else if !self.all_components_have_nominated_valid_pair() {
1087 self.pairs
1094 .iter_mut()
1095 .find(|p| p.state == CandidatePairState::Waiting)
1096 } else {
1097 None
1098 };
1099
1100 if let Some(pair) = pair {
1101 log::debug!(
1102 "start connectivity check for pair {}",
1103 DisplayPair(
1104 &self.local_candidates[pair.local],
1105 &self.remote_candidates[pair.remote]
1106 )
1107 );
1108
1109 let transaction_id = TransactionId::random();
1110
1111 let stun_request = stun::make_binding_request(
1112 transaction_id,
1113 &self.local_credentials,
1114 remote_credentials,
1115 &self.local_candidates[pair.local],
1116 self.is_controlling,
1117 self.control_tie_breaker,
1118 pair.nomination == CandidatePairNomination::Nominated,
1119 );
1120
1121 let source = self.local_candidates[pair.local].base.ip();
1122 let target = self.remote_candidates[pair.remote].addr;
1123
1124 pair.state = CandidatePairState::InProgress {
1125 transaction_id,
1126 stun_request: stun_request.clone(),
1127 retransmit_at: now + self.stun_config.retransmit_delta(0),
1128 retransmits: 0,
1129 source,
1130 target,
1131 };
1132
1133 self.events.push_back(IceEvent::SendData {
1134 component: pair.component,
1135 data: stun_request,
1136 source: Some(source),
1137 target,
1138 });
1139 }
1140 }
1141
1142 fn poll_retransmit(&mut self, now: Instant) {
1144 for pair in &mut self.pairs {
1145 let CandidatePairState::InProgress {
1146 transaction_id: _,
1147 stun_request,
1148 retransmit_at,
1149 retransmits,
1150 source,
1151 target,
1152 } = &mut pair.state
1153 else {
1154 continue;
1155 };
1156
1157 if *retransmit_at > now {
1158 continue;
1159 }
1160
1161 if *retransmits >= self.stun_config.max_retransmits {
1162 log::debug!(
1163 "failed connectivity check for pair {}",
1164 DisplayPair(
1165 &self.local_candidates[pair.local],
1166 &self.remote_candidates[pair.remote]
1167 )
1168 );
1169
1170 pair.state = CandidatePairState::Failed;
1171 pair.nomination = CandidatePairNomination::None;
1172 continue;
1173 }
1174
1175 *retransmits += 1;
1176 *retransmit_at += self.stun_config.retransmit_delta(*retransmits);
1177
1178 log::trace!(
1179 "retransmitting connectivity check for pair {}",
1180 DisplayPair(
1181 &self.local_candidates[pair.local],
1182 &self.remote_candidates[pair.remote]
1183 )
1184 );
1185
1186 self.events.push_back(IceEvent::SendData {
1187 component: pair.component,
1188 data: stun_request.clone(),
1189 source: Some(*source),
1190 target: *target,
1191 });
1192 }
1193 }
1194
1195 fn poll_state(&mut self) {
1196 let mut all_completed = true;
1198 for stun_server in &self.stun_server {
1199 if !stun_server.is_completed() {
1200 all_completed = false;
1201 }
1202 }
1203
1204 if all_completed && self.gathering_state != IceGatheringState::Complete {
1205 self.events.push_back(IceEvent::GatheringStateChanged {
1206 old: self.gathering_state,
1207 new: IceGatheringState::Complete,
1208 });
1209
1210 self.gathering_state = IceGatheringState::Complete;
1211 } else if !all_completed && self.gathering_state != IceGatheringState::Gathering {
1212 self.events.push_back(IceEvent::GatheringStateChanged {
1213 old: self.gathering_state,
1214 new: IceGatheringState::Gathering,
1215 });
1216
1217 self.gathering_state = IceGatheringState::Gathering;
1218 }
1219
1220 if self.remote_credentials.is_none() {
1222 return;
1223 }
1224
1225 let mut has_rtp_nomination = false;
1227 let mut has_rtcp_nomination = false;
1228
1229 let mut rtp_in_progress = false;
1230 let mut rtcp_in_progress = false;
1231
1232 for pair in &self.pairs {
1233 if pair.nomination == CandidatePairNomination::Selected
1234 && matches!(pair.state, CandidatePairState::Succeeded)
1235 {
1236 match pair.component {
1237 Component::Rtp => has_rtp_nomination = true,
1238 Component::Rtcp => has_rtcp_nomination = true,
1239 }
1240 }
1241
1242 if matches!(
1243 pair.state,
1244 CandidatePairState::Waiting
1245 | CandidatePairState::InProgress { .. }
1246 | CandidatePairState::Succeeded
1247 ) {
1248 match pair.component {
1249 Component::Rtp => rtp_in_progress = true,
1250 Component::Rtcp => rtcp_in_progress = true,
1251 }
1252 }
1253 }
1254
1255 let has_nomination_for_all_components = if self.rtcp_mux {
1256 has_rtp_nomination
1257 } else {
1258 has_rtp_nomination && has_rtcp_nomination
1259 };
1260
1261 let still_possible = if self.rtcp_mux {
1262 rtp_in_progress
1263 } else {
1264 rtp_in_progress && rtcp_in_progress
1265 };
1266
1267 if has_nomination_for_all_components
1268 && self.connection_state != IceConnectionState::Connected
1269 {
1270 self.set_connection_state(IceConnectionState::Connected);
1271 } else if !has_nomination_for_all_components {
1272 if still_possible {
1273 match self.connection_state {
1274 IceConnectionState::New => {
1275 self.set_connection_state(IceConnectionState::Checking);
1276 }
1277 IceConnectionState::Checking => {}
1278 IceConnectionState::Connected => {
1279 self.set_connection_state(IceConnectionState::Disconnected);
1280 }
1281 IceConnectionState::Failed => {}
1282 IceConnectionState::Disconnected => {}
1283 }
1284 } else {
1285 self.set_connection_state(IceConnectionState::Failed);
1286 }
1287 }
1288 }
1289
1290 fn set_connection_state(&mut self, new: IceConnectionState) {
1291 if self.connection_state != new {
1292 self.events.push_back(IceEvent::ConnectionStateChanged {
1293 old: self.connection_state,
1294 new,
1295 });
1296 self.connection_state = new;
1297 }
1298 }
1299
1300 fn poll_nomination(&mut self) {
1302 self.poll_nomination_of_component(Component::Rtp);
1303
1304 if !self.rtcp_mux {
1305 self.poll_nomination_of_component(Component::Rtcp);
1306 }
1307 }
1308 fn poll_nomination_of_component(&mut self, component: Component) {
1310 if self.component_has_nominated_valid_pair(component) {
1311 return;
1312 }
1313
1314 if self.is_controlling {
1315 let best_pair = self
1318 .pairs
1319 .iter_mut()
1320 .filter(|p| {
1321 p.component == component && matches!(p.state, CandidatePairState::Succeeded)
1322 })
1323 .max_by_key(|p| p.priority);
1324
1325 let Some(pair) = best_pair else {
1326 return;
1328 };
1329
1330 log::debug!(
1331 "nominating {}",
1332 DisplayPair(
1333 &self.local_candidates[pair.local],
1334 &self.remote_candidates[pair.remote]
1335 )
1336 );
1337
1338 pair.nomination = CandidatePairNomination::Nominated;
1339
1340 self.triggered_check_queue
1342 .push_front((pair.local, pair.remote));
1343 } else {
1344 let pair = self
1348 .pairs
1349 .iter_mut()
1350 .filter(|p| {
1351 p.component == component
1352 && p.nomination == CandidatePairNomination::Selected
1353 && matches!(p.state, CandidatePairState::Succeeded)
1354 })
1355 .max_by_key(|p| p.priority);
1356
1357 let Some(pair) = pair else {
1358 return;
1360 };
1361
1362 log::debug!(
1363 "using pair {}",
1364 DisplayPair(
1365 &self.local_candidates[pair.local],
1366 &self.remote_candidates[pair.remote]
1367 )
1368 );
1369
1370 self.events.push_back(IceEvent::DiscoveredAddr {
1371 component,
1372 target: self.remote_candidates[pair.remote].addr,
1373 });
1374
1375 self.triggered_check_queue
1377 .retain(|(local_id, _)| self.local_candidates[*local_id].component != component);
1378 }
1379 }
1380
1381 fn all_components_have_nominated_valid_pair(&self) -> bool {
1382 self.component_has_nominated_valid_pair(Component::Rtp)
1383 && (self.rtcp_mux || self.component_has_nominated_valid_pair(Component::Rtcp))
1384 }
1385
1386 fn component_has_nominated_valid_pair(&self, component: Component) -> bool {
1387 self.pairs.iter().any(|p| {
1388 p.component == component
1389 && p.nomination == CandidatePairNomination::Selected
1390 && matches!(
1391 p.state,
1392 CandidatePairState::InProgress { .. } | CandidatePairState::Succeeded
1393 )
1394 })
1395 }
1396
1397 pub fn pop_event(&mut self) -> Option<IceEvent> {
1401 self.events.pop_front()
1402 }
1403
1404 pub fn timeout(&self, now: Instant) -> Option<Duration> {
1406 let ta = if self.remote_credentials.is_some()
1408 && !self.all_components_have_nominated_valid_pair()
1409 {
1410 Some(
1411 self.last_ta_trigger
1412 .map(|it| {
1413 let poll_at = it + Duration::from_millis(50);
1414 poll_at.checked_duration_since(now).unwrap_or_default()
1415 })
1416 .unwrap_or_default(),
1417 )
1418 } else {
1419 None
1420 };
1421
1422 let pair_retransmit = self
1424 .pairs
1425 .iter()
1426 .filter_map(|p| {
1427 if let CandidatePairState::InProgress { retransmit_at, .. } = &p.state {
1428 retransmit_at.checked_duration_since(now)
1429 } else {
1430 None
1431 }
1432 })
1433 .min();
1434
1435 let stun_bindings = self.stun_server.iter().filter_map(|b| b.timeout(now)).min();
1437
1438 opt_min(opt_min(ta, pair_retransmit), stun_bindings)
1439 }
1440
1441 pub fn ice_candidates(&self) -> Vec<IceCandidate> {
1443 self.local_candidates
1444 .values()
1445 .filter(|c| matches!(c.kind, CandidateKind::Host | CandidateKind::ServerReflexive))
1446 .map(|c| {
1447 let rel_addr = if c.kind == CandidateKind::ServerReflexive {
1448 Some(c.base)
1449 } else {
1450 None
1451 };
1452
1453 IceCandidate {
1454 foundation: c.foundation.clone().into(),
1455 component: c.component as _,
1456 transport: "UDP".into(),
1457 priority: c.priority.into(),
1458 address: UntaggedAddress::IpAddress(c.addr.ip()),
1459 port: c.addr.port(),
1460 typ: match c.kind {
1461 CandidateKind::Host => "host".into(),
1462 CandidateKind::ServerReflexive => "srflx".into(),
1463 _ => unreachable!(),
1464 },
1465 rel_addr: rel_addr.map(|addr| UntaggedAddress::IpAddress(addr.ip())),
1466 rel_port: rel_addr.map(|addr| addr.port()),
1467 unknown: vec![],
1468 }
1469 })
1470 .collect()
1471 }
1472}
1473
1474fn pair_priority(
1475 local_candidate: &Candidate,
1476 remote_candidate: &Candidate,
1477 is_controlling: bool,
1478) -> u64 {
1479 let (g, d) = if is_controlling {
1480 (
1481 local_candidate.priority as u64,
1482 remote_candidate.priority as u64,
1483 )
1484 } else {
1485 (
1486 remote_candidate.priority as u64,
1487 local_candidate.priority as u64,
1488 )
1489 };
1490
1491 2u64.pow(32) * min(g, d) + 2 * max(g, d) + if g > d { 1 } else { 0 }
1493}
1494
1495fn compute_foundation(
1496 kind: CandidateKind,
1497 base: IpAddr,
1498 stun_server: Option<SocketAddr>,
1500 proto: &str,
1501) -> u64 {
1502 let mut hasher = DefaultHasher::new();
1503 (kind, base, stun_server, proto).hash(&mut hasher);
1504 hasher.finish()
1505}
1506
1507struct DisplayPair<'a>(&'a Candidate, &'a Candidate);
1508
1509impl fmt::Display for DisplayPair<'_> {
1510 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1511 fmt_candidate(f, self.0)?;
1512 write!(f, " <-> ")?;
1513 fmt_candidate(f, self.1)
1514 }
1515}
1516
1517struct DisplayCandidate<'a>(&'a Candidate);
1518
1519impl fmt::Display for DisplayCandidate<'_> {
1520 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1521 fmt_candidate(f, self.0)
1522 }
1523}
1524
1525fn fmt_candidate(f: &mut fmt::Formatter<'_>, c: &Candidate) -> fmt::Result {
1526 match c.kind {
1527 CandidateKind::Host => write!(f, "host({})", c.addr),
1528 CandidateKind::PeerReflexive => {
1529 write!(f, "peer-reflexive(base:{}, peer:{})", c.base, c.addr)
1530 }
1531 CandidateKind::ServerReflexive => {
1532 write!(f, "server-reflexive(base:{}, server:{})", c.base, c.addr)
1533 } }
1535}
1536
1537fn opt_min<T: Ord>(a: Option<T>, b: Option<T>) -> Option<T> {
1538 match (a, b) {
1539 (None, None) => None,
1540 (None, Some(b)) => Some(b),
1541 (Some(a), None) => Some(a),
1542 (Some(a), Some(b)) => Some(min(a, b)),
1543 }
1544}