1use alloc::boxed::Box;
14use alloc::vec::Vec;
15
16use core::error::Error;
17use core::fmt::Display;
18use core::net::SocketAddr;
19use core::sync::atomic::AtomicU64;
20use core::time::Duration;
21
22use stun_proto::Instant;
23use stun_proto::types::data::Data;
24
25use crate::candidate::{ParseCandidateError, TransportType};
26use crate::component::ComponentConnectionState;
27use crate::conncheck::{
28 CheckListSetPollRet, ConnCheckEvent, ConnCheckListSet, RequestRto, SelectedPair,
29};
30use crate::gathering::{GatherPoll, GatheredCandidate};
31use crate::rand::rand_u64;
32use crate::stream::{Stream, StreamMut, StreamState};
33use crate::turn::TurnConfig;
34use stun_proto::agent::{StunError, Transmit};
35use stun_proto::types::message::StunParseError;
36
37use tracing::{info, warn};
38
39#[derive(Debug)]
41pub enum AgentError {
42 Failed,
44 AlreadyExists,
46 AlreadyInProgress,
48 NotInProgress,
50 ResourceNotFound,
52 Malformed,
54 WrongImplementation,
56 ConnectionClosed,
58 StunParse,
60 StunWrite,
62 CandidateParse(ParseCandidateError),
64 ProtocolViolation,
66}
67
68impl Error for AgentError {}
69
70impl Display for AgentError {
71 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
72 write!(f, "{self:?}")
73 }
74}
75
76impl From<ParseCandidateError> for AgentError {
77 fn from(e: ParseCandidateError) -> Self {
78 Self::CandidateParse(e)
79 }
80}
81
82impl From<StunError> for AgentError {
83 fn from(e: StunError) -> Self {
84 match e {
85 StunError::ResourceNotFound => AgentError::ResourceNotFound,
86 StunError::ProtocolViolation => AgentError::ProtocolViolation,
87 StunError::ParseError(_) => AgentError::StunParse,
88 StunError::WriteError(_) => AgentError::StunWrite,
89 StunError::AlreadyInProgress => AgentError::AlreadyInProgress,
90 _ => AgentError::Failed,
91 }
92 }
93}
94
95impl From<StunParseError> for AgentError {
96 fn from(_e: StunParseError) -> Self {
97 Self::StunParse
98 }
99}
100
101#[derive(Debug)]
103pub struct Agent {
104 id: u64,
105 pub(crate) checklistset: ConnCheckListSet,
106 pub(crate) stun_servers: Vec<(TransportType, SocketAddr)>,
107 pub(crate) turn_servers: Vec<TurnConfig>,
108 streams: Vec<StreamState>,
109 pub(crate) rto: Option<RequestRto>,
110}
111
112#[derive(Debug)]
114pub struct AgentBuilder {
115 trickle_ice: bool,
116 controlling: bool,
117 timing_advance: Duration,
118 rto: Option<RequestRto>,
119}
120
121impl Default for AgentBuilder {
122 fn default() -> Self {
123 Self {
124 trickle_ice: false,
125 controlling: false,
126 timing_advance: crate::conncheck::DEFAULT_MINIMUM_SET_TICK,
127 rto: None,
128 }
129 }
130}
131
132impl AgentBuilder {
133 pub fn trickle_ice(mut self, trickle_ice: bool) -> Self {
135 self.trickle_ice = trickle_ice;
136 self
137 }
138
139 pub fn controlling(mut self, controlling: bool) -> Self {
142 self.controlling = controlling;
143 self
144 }
145
146 pub fn timing_advance(mut self, ta: Duration) -> Self {
152 self.timing_advance = ta;
153 self
154 }
155
156 pub fn set_request_retransmits(
172 mut self,
173 initial: Duration,
174 max: Duration,
175 retransmits: u32,
176 final_retransmit_timeout: Duration,
177 ) -> Self {
178 self.rto = Some(RequestRto::from_parts(
179 initial,
180 max,
181 retransmits,
182 final_retransmit_timeout,
183 ));
184 self
185 }
186
187 pub fn build(self) -> Agent {
189 turn_client_proto::types::debug_init();
190 rice_stun_types::debug_init();
191
192 let id = AGENT_COUNT.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
193 let tie_breaker = rand_u64();
194 let controlling = self.controlling;
195 let mut checklistset = ConnCheckListSet::builder(tie_breaker, controlling)
196 .trickle_ice(self.trickle_ice)
197 .timing_advance(self.timing_advance)
198 .build();
199 if let Some(rto) = self.rto.clone() {
200 checklistset.set_request_retransmits(rto);
201 }
202 Agent {
203 id,
204 checklistset,
205 stun_servers: Vec::new(),
206 turn_servers: Vec::new(),
207 streams: Vec::new(),
208 rto: self.rto,
209 }
210 }
211}
212
213static AGENT_COUNT: AtomicU64 = AtomicU64::new(0);
214
215impl Default for Agent {
216 fn default() -> Self {
217 Agent::builder().build()
218 }
219}
220
221impl Agent {
222 pub fn builder() -> AgentBuilder {
224 AgentBuilder::default()
225 }
226
227 pub fn id(&self) -> u64 {
229 self.id
230 }
231
232 pub fn timing_advance(&self) -> Duration {
238 self.checklistset.timing_advance()
239 }
240
241 pub fn set_timing_advance(&mut self, ta: Duration) {
247 self.checklistset.set_timing_advance(ta)
248 }
249
250 pub fn set_request_retransmits(
266 &mut self,
267 initial: Duration,
268 max: Duration,
269 retransmits: u32,
270 final_retransmit_timeout: Duration,
271 ) {
272 let rto = RequestRto::from_parts(initial, max, retransmits, final_retransmit_timeout);
273 for stream in self.streams.iter_mut() {
274 stream.set_request_retransmits(rto.clone());
275 }
276 self.checklistset.set_request_retransmits(rto);
277 }
278
279 #[tracing::instrument(
291 name = "ice_add_stream",
292 skip(self),
293 fields(
294 ice.id = self.id
295 )
296 )]
297 pub fn add_stream(&mut self) -> usize {
298 let checklist_id = self.checklistset.new_list();
299 let id = self.streams.len();
300 let stream = crate::stream::StreamState::new(id, checklist_id);
301 self.streams.push(stream);
302 id
303 }
304
305 #[tracing::instrument(
308 name = "ice_close",
309 skip(self),
310 fields(
311 ice.id = self.id
312 )
313 )]
314 pub fn close(&mut self, now: Instant) {
315 info!("closing agent");
316 self.checklistset.close(now);
317 }
318
319 pub fn controlling(&self) -> bool {
322 self.checklistset.controlling()
323 }
324
325 #[tracing::instrument(
327 name = "ice_add_stun_server",
328 skip(self)
329 fields(ice.id = self.id)
330 )]
331 pub fn add_stun_server(&mut self, transport: TransportType, addr: SocketAddr) {
332 info!("Adding stun server");
333 self.stun_servers.push((transport, addr));
334 }
336
337 pub fn stun_servers(&self) -> &[(TransportType, SocketAddr)] {
339 &self.stun_servers
340 }
341
342 #[tracing::instrument(
345 name = "ice_add_turn_server",
346 skip(self)
347 fields(ice.id = self.id)
348 )]
349 pub fn add_turn_server(&mut self, config: TurnConfig) {
350 info!("Adding turn server");
351 self.turn_servers.push(config);
352 }
354
355 pub fn turn_servers(&self) -> &[TurnConfig] {
357 &self.turn_servers
358 }
359
360 pub fn stream(&self, id: usize) -> Option<crate::stream::Stream<'_>> {
364 if self.streams.get(id).is_some() {
365 Some(Stream::from_agent(self, id))
366 } else {
367 None
368 }
369 }
370
371 pub(crate) fn stream_state(&self, id: usize) -> Option<&crate::stream::StreamState> {
372 self.streams.get(id)
373 }
374
375 pub fn mut_stream(&mut self, id: usize) -> Option<StreamMut<'_>> {
377 if self.streams.get_mut(id).is_some() {
378 Some(StreamMut::from_agent(self, id))
379 } else {
380 None
381 }
382 }
383
384 pub(crate) fn mut_stream_state(
385 &mut self,
386 id: usize,
387 ) -> Option<&mut crate::stream::StreamState> {
388 self.streams.get_mut(id)
389 }
390
391 #[tracing::instrument(
394 name = "agent_poll",
395 ret
396 skip(self)
397 fields(
398 id = self.id,
399 )
400 )]
401 pub fn poll(&mut self, now: Instant) -> AgentPoll {
402 let mut lowest_wait = None;
403
404 for stream in self.streams.iter_mut() {
405 let stream_id = stream.id();
406 match stream.poll_gather(now) {
407 GatherPoll::AllocateSocket {
408 component_id,
409 transport,
410 local_addr,
411 remote_addr,
412 } => {
413 return AgentPoll::AllocateSocket(AgentSocket {
414 stream_id,
415 component_id,
416 transport,
417 from: local_addr,
418 to: remote_addr,
419 });
420 }
421 GatherPoll::WaitUntil(earliest_wait) => {
422 if let Some(check_wait) = lowest_wait {
423 if earliest_wait < check_wait {
424 lowest_wait = Some(earliest_wait);
425 }
426 } else {
427 lowest_wait = Some(earliest_wait);
428 }
429 }
430 GatherPoll::NewCandidate(candidate) => {
431 return AgentPoll::GatheredCandidate(AgentGatheredCandidate {
432 stream_id,
433 gathered: candidate,
434 });
435 }
436 GatherPoll::Complete(component_id) => {
437 return AgentPoll::GatheringComplete(AgentGatheringComplete {
438 stream_id,
439 component_id,
440 });
441 }
442 GatherPoll::Finished => (),
443 }
444 }
445
446 loop {
447 match self.checklistset.poll(now) {
448 CheckListSetPollRet::Closed => return AgentPoll::Closed,
449 CheckListSetPollRet::Completed => continue,
450 CheckListSetPollRet::WaitUntil(earliest_wait) => {
451 if let Some(check_wait) = lowest_wait {
452 if earliest_wait < check_wait {
453 lowest_wait = Some(earliest_wait);
454 }
455 } else {
456 lowest_wait = Some(earliest_wait);
457 }
458 break;
459 }
460 CheckListSetPollRet::AllocateSocket {
461 checklist_id,
462 component_id: cid,
463 transport,
464 local_addr: from,
465 remote_addr: to,
466 } => {
467 if let Some(stream) =
468 self.streams.iter().find(|s| s.checklist_id == checklist_id)
469 {
470 return AgentPoll::AllocateSocket(AgentSocket {
471 stream_id: stream.id(),
472 component_id: cid,
473 transport,
474 from,
475 to,
476 });
477 } else {
478 warn!("did not find stream for allocate socket {from:?} -> {to:?}");
479 }
480 }
481 CheckListSetPollRet::RemoveSocket {
482 checklist_id,
483 component_id: cid,
484 transport,
485 local_addr: from,
486 remote_addr: to,
487 } => {
488 if let Some(stream) =
489 self.streams.iter().find(|s| s.checklist_id == checklist_id)
490 {
491 return AgentPoll::RemoveSocket(AgentSocket {
492 stream_id: stream.id(),
493 component_id: cid,
494 transport,
495 from,
496 to,
497 });
498 } else {
499 warn!("did not find stream for remove socket {from:?} -> {to:?}");
500 }
501 }
502 CheckListSetPollRet::Event {
503 checklist_id,
504 event: ConnCheckEvent::ComponentState(cid, state),
505 } => {
506 if let Some(stream) = self
507 .streams
508 .iter_mut()
509 .find(|s| s.checklist_id == checklist_id)
510 {
511 if let Some(component) = stream.mut_component_state(cid) {
512 if component.set_state(state) {
513 return AgentPoll::ComponentStateChange(
514 AgentComponentStateChange {
515 stream_id: stream.id(),
516 component_id: cid,
517 state,
518 },
519 );
520 }
521 }
522 }
523 }
524 CheckListSetPollRet::Event {
525 checklist_id,
526 event: ConnCheckEvent::SelectedPair(cid, selected),
527 } => {
528 if let Some(stream) =
529 self.streams.iter().find(|s| s.checklist_id == checklist_id)
530 {
531 if stream.component_state(cid).is_some() {
532 return AgentPoll::SelectedPair(AgentSelectedPair {
533 stream_id: stream.id(),
534 component_id: cid,
535 selected,
536 });
537 }
538 }
539 }
540 }
541 }
542
543 AgentPoll::WaitUntil(lowest_wait.unwrap_or_else(|| now + Duration::from_secs(600)))
544 }
545
546 pub fn poll_transmit(&mut self, now: Instant) -> Option<AgentTransmit> {
551 for stream in self.streams.iter_mut() {
552 let stream_id = stream.id();
553 if let Some((_component_id, transmit)) = stream.poll_gather_transmit(now) {
554 return Some(AgentTransmit::from_data(stream_id, transmit));
555 }
556 }
557 let transmit = self.checklistset.poll_transmit(now)?;
558 if let Some(stream) = self
559 .streams
560 .iter()
561 .find(|s| s.checklist_id == transmit.checklist_id)
562 {
563 Some(AgentTransmit {
564 stream_id: stream.id(),
565 transmit: transmit.transmit,
566 })
567 } else {
568 warn!(
569 "did not find stream for transmit {:?} -> {:?}",
570 transmit.transmit.from, transmit.transmit.to
571 );
572 None
573 }
574 }
575}
576
577#[derive(Debug)]
579pub enum AgentPoll {
580 Closed,
582 WaitUntil(Instant),
584 AllocateSocket(AgentSocket),
587 RemoveSocket(AgentSocket),
590 SelectedPair(AgentSelectedPair),
592 ComponentStateChange(AgentComponentStateChange),
594 GatheredCandidate(AgentGatheredCandidate),
596 GatheringComplete(AgentGatheringComplete),
598}
599
600#[derive(Debug)]
602pub struct AgentTransmit {
603 pub stream_id: usize,
605 pub transmit: Transmit<Box<[u8]>>,
607}
608
609impl AgentTransmit {
610 fn from_data(stream_id: usize, transmit: Transmit<Data<'_>>) -> Self {
611 Self {
612 stream_id,
613 transmit: transmit.reinterpret_data(|data| {
614 let Data::Owned(owned) = data.into_owned() else {
615 unreachable!();
616 };
617 owned.take()
618 }),
619 }
620 }
621}
622
623#[derive(Debug)]
625pub struct AgentSocket {
626 pub stream_id: usize,
628 pub component_id: usize,
630 pub transport: TransportType,
632 pub from: SocketAddr,
634 pub to: SocketAddr,
636}
637
638#[derive(Debug)]
640pub struct AgentSelectedPair {
641 pub stream_id: usize,
643 pub component_id: usize,
645 pub selected: Box<SelectedPair>,
647}
648
649#[derive(Debug)]
651#[repr(C)]
652pub struct AgentComponentStateChange {
653 pub stream_id: usize,
655 pub component_id: usize,
657 pub state: ComponentConnectionState,
659}
660
661#[derive(Debug)]
663#[repr(C)]
664pub struct AgentGatheredCandidate {
665 pub stream_id: usize,
667 pub gathered: GatheredCandidate,
669}
670
671#[derive(Debug)]
673#[repr(C)]
674pub struct AgentGatheringComplete {
675 pub stream_id: usize,
677 pub component_id: usize,
679}
680
681#[cfg(test)]
682mod tests {
683 use super::*;
684
685 #[test]
686 fn controlling() {
687 let _log = crate::tests::test_init_log();
688 let agent = Agent::builder().controlling(true).build();
689 assert!(agent.controlling());
690 let agent = Agent::builder().controlling(false).build();
691 assert!(!agent.controlling());
692 }
693
694 #[test]
695 fn timing_advance() {
696 let _log = crate::tests::test_init_log();
697 let ta = Duration::from_secs(1);
698 let default_ta = Duration::from_millis(50);
699 let mut agent = Agent::default();
700 assert_eq!(agent.timing_advance(), default_ta);
701 agent.set_timing_advance(ta);
702 assert_eq!(agent.timing_advance(), ta);
703 let agent = Agent::builder().timing_advance(ta).build();
704 assert_eq!(agent.timing_advance(), ta);
705 }
706}