1use std::net::SocketAddr;
8
9use std::future::Future;
10#[cfg(feature = "dtls")]
11use std::io::{Read, Write};
12use std::sync::atomic::{AtomicUsize, Ordering};
13use std::sync::{Arc, Mutex as StdMutex};
14#[cfg(feature = "dtls")]
15use std::time::Duration;
16
17use sipx_sdp::ice::CandidateType;
18#[cfg(feature = "dtls")]
19use tokio::net::UdpSocket;
20#[cfg(feature = "dtls")]
21use tokio::sync::{Mutex, mpsc};
22
23#[cfg(all(test, feature = "dtls"))]
24use std::sync::atomic::AtomicBool;
25#[cfg(all(test, feature = "dtls"))]
26use tokio::sync::Notify;
27
28#[cfg(all(test, feature = "dtls"))]
29static ACTIVE_SUPERVISORS: AtomicUsize = AtomicUsize::new(0);
30#[cfg(all(test, feature = "dtls"))]
31static DTLS_HANDSHAKING: AtomicBool = AtomicBool::new(false);
32#[cfg(all(test, feature = "dtls"))]
33static SUPERVISOR_CHANGED: Notify = Notify::const_new();
34#[cfg(all(test, feature = "dtls"))]
35static HANDSHAKE_STARTED: Notify = Notify::const_new();
36
37pub const MAX_DATAGRAM: usize = 2048;
39
40pub const MAX_PROFILE_TASKS: usize = 6;
42
43#[derive(Debug, Default)]
45pub(crate) struct ProfileTasks {
46 active: AtomicUsize,
47 peak: AtomicUsize,
48}
49
50impl ProfileTasks {
51 fn enter(self: &Arc<Self>) -> ProfileTaskPermit {
52 let active = self.active.fetch_add(1, Ordering::SeqCst) + 1;
53 self.peak.fetch_max(active, Ordering::SeqCst);
54 ProfileTaskPermit {
55 tasks: Arc::clone(self),
56 }
57 }
58
59 #[cfg(all(test, feature = "dtls"))]
60 pub(crate) fn counts(&self) -> (usize, usize) {
61 (
62 self.active.load(Ordering::SeqCst),
63 self.peak.load(Ordering::SeqCst),
64 )
65 }
66}
67
68struct ProfileTaskPermit {
69 tasks: Arc<ProfileTasks>,
70}
71
72impl Drop for ProfileTaskPermit {
73 fn drop(&mut self) {
74 self.tasks.active.fetch_sub(1, Ordering::SeqCst);
75 }
76}
77
78pub(crate) async fn profile_task<F: Future>(tasks: Arc<ProfileTasks>, future: F) -> F::Output {
79 let _permit = tasks.enter();
80 future.await
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum IngressClass {
86 Stun,
88 Dtls,
90 Srtp,
92 Srtcp,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98#[non_exhaustive]
99pub enum IngressDrop {
100 Empty,
102 TruncatedClassPrefix,
104 Oversized,
106 UnknownProtocol,
108 BeforeNomination,
110 WrongPeer,
112 KeysUnavailable,
114 UnexpectedDtls,
116 Closed,
118}
119
120pub fn classify_datagram(datagram: &[u8]) -> Result<IngressClass, IngressDrop> {
124 if datagram.len() > MAX_DATAGRAM {
125 return Err(IngressDrop::Oversized);
126 }
127 let first = *datagram.first().ok_or(IngressDrop::Empty)?;
128 match first {
129 0..=1 => Ok(IngressClass::Stun),
130 20..=63 => Ok(IngressClass::Dtls),
131 128..=191 => {
132 let second = *datagram.get(1).ok_or(IngressDrop::TruncatedClassPrefix)?;
133 if (192..=223).contains(&second) {
134 Ok(IngressClass::Srtcp)
135 } else {
136 Ok(IngressClass::Srtp)
137 }
138 }
139 _ => Err(IngressDrop::UnknownProtocol),
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub struct SelectedComponent {
146 pub local: SocketAddr,
148 pub remote: SocketAddr,
150 pub ice_generation: u64,
152 pub local_kind: CandidateType,
154 pub remote_kind: CandidateType,
156}
157
158impl SelectedComponent {
159 #[must_use]
161 pub const fn new(local: SocketAddr, remote: SocketAddr, ice_generation: u64) -> Self {
162 Self {
163 local,
164 remote,
165 ice_generation,
166 local_kind: CandidateType::Host,
167 remote_kind: CandidateType::Host,
168 }
169 }
170
171 #[must_use]
173 pub const fn with_candidate_types(
174 mut self,
175 local_kind: CandidateType,
176 remote_kind: CandidateType,
177 ) -> Self {
178 self.local_kind = local_kind;
179 self.remote_kind = remote_kind;
180 self
181 }
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186pub enum ComponentState {
187 IceChecking,
189 Nominated,
191 DtlsHandshaking,
193 KeysInstalled,
195 Running,
197 Closed,
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
203pub struct IngressCounts {
204 pub ingress_empty: u64,
206 pub ingress_truncated_prefix: u64,
208 pub ingress_oversized: u64,
210 pub ingress_unknown_protocol: u64,
212 pub dtls_before_nomination: u64,
214 pub srtp_before_nomination: u64,
216 pub srtcp_before_nomination: u64,
218 pub dtls_wrong_peer: u64,
220 pub srtp_wrong_peer: u64,
222 pub srtcp_wrong_peer: u64,
224 pub srtp_keys_unavailable: u64,
226 pub srtcp_keys_unavailable: u64,
228 pub dtls_unexpected_records: u64,
230 pub ingress_closed: u64,
232 pub stun_malformed: u64,
234 pub dtls_malformed: u64,
236 pub srtp_malformed: u64,
238 pub srtcp_malformed: u64,
240 pub stun_queue_refusals: u64,
242 pub dtls_queue_refusals: u64,
244 pub srtp_queue_refusals: u64,
246 pub srtcp_queue_refusals: u64,
248 pub srtp_authentication_failures: u64,
250 pub srtcp_authentication_failures: u64,
252 pub stun_authentication_failures: u64,
254 pub srtp_replays: u64,
256 pub srtcp_replays: u64,
258 pub srtcp_processed: u64,
260}
261
262impl IngressCounts {
263 #[must_use]
265 pub fn total(self) -> u64 {
266 [
267 self.ingress_empty,
268 self.ingress_truncated_prefix,
269 self.ingress_oversized,
270 self.ingress_unknown_protocol,
271 self.dtls_before_nomination,
272 self.srtp_before_nomination,
273 self.srtcp_before_nomination,
274 self.dtls_wrong_peer,
275 self.srtp_wrong_peer,
276 self.srtcp_wrong_peer,
277 self.srtp_keys_unavailable,
278 self.srtcp_keys_unavailable,
279 self.dtls_unexpected_records,
280 self.ingress_closed,
281 self.stun_malformed,
282 self.dtls_malformed,
283 self.srtp_malformed,
284 self.srtcp_malformed,
285 self.stun_queue_refusals,
286 self.dtls_queue_refusals,
287 self.srtp_queue_refusals,
288 self.srtcp_queue_refusals,
289 self.srtp_authentication_failures,
290 self.srtcp_authentication_failures,
291 self.stun_authentication_failures,
292 self.srtp_replays,
293 self.srtcp_replays,
294 ]
295 .into_iter()
296 .fold(0, u64::saturating_add)
297 }
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq)]
302pub enum IngressDisposition {
303 Accepted(IngressClass),
305 Dropped(IngressDrop),
307}
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq)]
311pub struct BrowserComponentSnapshot {
312 pub state: ComponentState,
314 pub selected: Option<SelectedComponent>,
316 pub counts: IngressCounts,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
322#[non_exhaustive]
323pub enum ComponentError {
324 #[error("ICE nomination generation {got} does not match current generation {expected}")]
326 Generation {
327 expected: u64,
329 got: u64,
331 },
332 #[error("component operation {operation} is invalid in state {state:?}")]
334 State {
335 operation: &'static str,
337 state: ComponentState,
339 },
340 #[error("DTLS peer {got} is not nominated peer {expected}")]
342 WrongPeer {
343 expected: SocketAddr,
345 got: SocketAddr,
347 },
348}
349
350#[derive(Debug)]
352pub struct ComponentIngress {
353 generation: u64,
354 state: ComponentState,
355 selected: Option<SelectedComponent>,
356 keys: Option<crate::SrtpKeys>,
357 counts: IngressCounts,
358}
359
360impl ComponentIngress {
361 #[must_use]
363 pub const fn new(generation: u64) -> Self {
364 Self {
365 generation,
366 state: ComponentState::IceChecking,
367 selected: None,
368 keys: None,
369 counts: IngressCounts {
370 ingress_empty: 0,
371 ingress_truncated_prefix: 0,
372 ingress_oversized: 0,
373 ingress_unknown_protocol: 0,
374 dtls_before_nomination: 0,
375 srtp_before_nomination: 0,
376 srtcp_before_nomination: 0,
377 dtls_wrong_peer: 0,
378 srtp_wrong_peer: 0,
379 srtcp_wrong_peer: 0,
380 srtp_keys_unavailable: 0,
381 srtcp_keys_unavailable: 0,
382 dtls_unexpected_records: 0,
383 ingress_closed: 0,
384 stun_malformed: 0,
385 dtls_malformed: 0,
386 srtp_malformed: 0,
387 srtcp_malformed: 0,
388 stun_queue_refusals: 0,
389 dtls_queue_refusals: 0,
390 srtp_queue_refusals: 0,
391 srtcp_queue_refusals: 0,
392 srtp_authentication_failures: 0,
393 srtcp_authentication_failures: 0,
394 stun_authentication_failures: 0,
395 srtp_replays: 0,
396 srtcp_replays: 0,
397 srtcp_processed: 0,
398 },
399 }
400 }
401
402 pub fn nominate(&mut self, selected: SelectedComponent) -> Result<(), ComponentError> {
404 if selected.ice_generation != self.generation {
405 return Err(ComponentError::Generation {
406 expected: self.generation,
407 got: selected.ice_generation,
408 });
409 }
410 if self.state != ComponentState::IceChecking {
411 return Err(ComponentError::State {
412 operation: "nominate",
413 state: self.state,
414 });
415 }
416 self.selected = Some(selected);
417 self.state = ComponentState::Nominated;
418 Ok(())
419 }
420
421 pub fn begin_dtls(&mut self, peer: SocketAddr) -> Result<(), ComponentError> {
423 if self.state != ComponentState::Nominated {
424 return Err(ComponentError::State {
425 operation: "begin_dtls",
426 state: self.state,
427 });
428 }
429 let expected =
430 self.selected
431 .map(|selected| selected.remote)
432 .ok_or(ComponentError::State {
433 operation: "begin_dtls",
434 state: self.state,
435 })?;
436 if peer != expected {
437 return Err(ComponentError::WrongPeer {
438 expected,
439 got: peer,
440 });
441 }
442 self.state = ComponentState::DtlsHandshaking;
443 Ok(())
444 }
445
446 pub fn install_verified_keys(
448 &mut self,
449 keys: crate::dtls::VerifiedKeys,
450 ) -> Result<(), ComponentError> {
451 if self.state != ComponentState::DtlsHandshaking {
452 return Err(ComponentError::State {
453 operation: "install_verified_keys",
454 state: self.state,
455 });
456 }
457 self.keys = Some(keys.into_srtp_keys());
458 self.state = ComponentState::KeysInstalled;
459 Ok(())
460 }
461
462 pub fn start_media(&mut self) -> Result<crate::SrtpKeys, ComponentError> {
464 if self.state != ComponentState::KeysInstalled {
465 return Err(ComponentError::State {
466 operation: "start_media",
467 state: self.state,
468 });
469 }
470 let keys = self.keys.take().ok_or(ComponentError::State {
471 operation: "start_media",
472 state: self.state,
473 })?;
474 self.state = ComponentState::Running;
475 Ok(keys)
476 }
477
478 pub fn admit(&mut self, source: SocketAddr, datagram: &[u8]) -> IngressDisposition {
480 let class = match classify_datagram(datagram) {
481 Ok(class) => class,
482 Err(reason) => {
483 self.note_drop(None, reason);
484 return IngressDisposition::Dropped(reason);
485 }
486 };
487 let reason = if self.state == ComponentState::Closed {
488 Some(IngressDrop::Closed)
489 } else if class == IngressClass::Stun {
490 None
491 } else if self.selected.is_none() {
492 Some(IngressDrop::BeforeNomination)
493 } else if self
494 .selected
495 .is_some_and(|selected| selected.remote != source)
496 {
497 Some(IngressDrop::WrongPeer)
498 } else {
499 match class {
500 IngressClass::Stun => None,
501 IngressClass::Dtls => (!matches!(
502 self.state,
503 ComponentState::Nominated | ComponentState::DtlsHandshaking
504 ))
505 .then_some(IngressDrop::UnexpectedDtls),
506 IngressClass::Srtp | IngressClass::Srtcp => {
507 (self.state != ComponentState::Running).then_some(IngressDrop::KeysUnavailable)
508 }
509 }
510 };
511 if let Some(reason) = reason {
512 self.note_drop(Some(class), reason);
513 IngressDisposition::Dropped(reason)
514 } else {
515 IngressDisposition::Accepted(class)
516 }
517 }
518
519 pub fn close(&mut self) {
521 self.keys = None;
522 self.state = ComponentState::Closed;
523 }
524
525 #[must_use]
527 pub const fn snapshot(&self) -> BrowserComponentSnapshot {
528 BrowserComponentSnapshot {
529 state: self.state,
530 selected: self.selected,
531 counts: self.counts,
532 }
533 }
534
535 pub(crate) fn note_malformed(&mut self, class: IngressClass) {
536 let counter = match class {
537 IngressClass::Stun => &mut self.counts.stun_malformed,
538 IngressClass::Dtls => &mut self.counts.dtls_malformed,
539 IngressClass::Srtp => &mut self.counts.srtp_malformed,
540 IngressClass::Srtcp => &mut self.counts.srtcp_malformed,
541 };
542 *counter = counter.saturating_add(1);
543 }
544
545 #[cfg(feature = "dtls")]
546 pub(crate) fn note_queue_full(&mut self, class: IngressClass) {
547 let counter = match class {
548 IngressClass::Stun => &mut self.counts.stun_queue_refusals,
549 IngressClass::Dtls => &mut self.counts.dtls_queue_refusals,
550 IngressClass::Srtp => &mut self.counts.srtp_queue_refusals,
551 IngressClass::Srtcp => &mut self.counts.srtcp_queue_refusals,
552 };
553 *counter = counter.saturating_add(1);
554 }
555
556 pub(crate) fn note_authentication_failure(&mut self, class: IngressClass) {
557 let counter = match class {
558 IngressClass::Stun => &mut self.counts.stun_authentication_failures,
559 IngressClass::Srtp => &mut self.counts.srtp_authentication_failures,
560 IngressClass::Srtcp => &mut self.counts.srtcp_authentication_failures,
561 IngressClass::Dtls => return,
562 };
563 *counter = counter.saturating_add(1);
564 }
565
566 pub(crate) fn note_replay(&mut self, class: IngressClass) {
567 let counter = match class {
568 IngressClass::Srtp => &mut self.counts.srtp_replays,
569 IngressClass::Srtcp => &mut self.counts.srtcp_replays,
570 IngressClass::Stun | IngressClass::Dtls => return,
571 };
572 *counter = counter.saturating_add(1);
573 }
574
575 pub(crate) fn note_srtcp_processed(&mut self) {
576 self.counts.srtcp_processed = self.counts.srtcp_processed.saturating_add(1);
577 }
578
579 fn note_drop(&mut self, class: Option<IngressClass>, reason: IngressDrop) {
580 let counter = match (class, reason) {
581 (_, IngressDrop::Empty) => &mut self.counts.ingress_empty,
582 (_, IngressDrop::TruncatedClassPrefix) => &mut self.counts.ingress_truncated_prefix,
583 (_, IngressDrop::Oversized) => &mut self.counts.ingress_oversized,
584 (_, IngressDrop::UnknownProtocol) => &mut self.counts.ingress_unknown_protocol,
585 (Some(IngressClass::Dtls), IngressDrop::BeforeNomination) => {
586 &mut self.counts.dtls_before_nomination
587 }
588 (Some(IngressClass::Srtp), IngressDrop::BeforeNomination) => {
589 &mut self.counts.srtp_before_nomination
590 }
591 (Some(IngressClass::Srtcp), IngressDrop::BeforeNomination) => {
592 &mut self.counts.srtcp_before_nomination
593 }
594 (Some(IngressClass::Dtls), IngressDrop::WrongPeer) => &mut self.counts.dtls_wrong_peer,
595 (Some(IngressClass::Srtp), IngressDrop::WrongPeer) => &mut self.counts.srtp_wrong_peer,
596 (Some(IngressClass::Srtcp), IngressDrop::WrongPeer) => {
597 &mut self.counts.srtcp_wrong_peer
598 }
599 (Some(IngressClass::Srtp), IngressDrop::KeysUnavailable) => {
600 &mut self.counts.srtp_keys_unavailable
601 }
602 (Some(IngressClass::Srtcp), IngressDrop::KeysUnavailable) => {
603 &mut self.counts.srtcp_keys_unavailable
604 }
605 (Some(IngressClass::Dtls), IngressDrop::UnexpectedDtls) => {
606 &mut self.counts.dtls_unexpected_records
607 }
608 (_, IngressDrop::Closed) => &mut self.counts.ingress_closed,
609 _ => return,
612 };
613 *counter = counter.saturating_add(1);
614 }
615}
616
617#[cfg(feature = "dtls")]
619#[derive(Debug, thiserror::Error)]
620#[non_exhaustive]
621pub enum BrowserStartError {
622 #[error("media setup: {0}")]
624 Setup(#[from] crate::SetupError),
625 #[error("browser audio requires RTP/RTCP multiplexing")]
627 RtcpMuxRequired,
628 #[error("ICE failed before nominating the browser-audio component")]
630 IceFailed,
631 #[error("ICE stopped before nominating the browser-audio component")]
633 IceStopped,
634 #[error("DTLS handshake exceeded its configured deadline")]
636 DtlsTimeout,
637 #[error("browser component transition: {0}")]
639 Component(#[from] ComponentError),
640 #[error("DTLS worker: {0}")]
642 Worker(String),
643 #[error("DTLS: {0}")]
645 Dtls(#[from] crate::dtls::Error),
646 #[error("DTLS adapter: {0}")]
648 Adapter(String),
649}
650
651#[cfg(feature = "dtls")]
652#[derive(Debug)]
653pub(crate) struct Datagram {
654 pub(crate) source: SocketAddr,
655 pub(crate) bytes: Vec<u8>,
656}
657
658#[cfg(feature = "dtls")]
660#[derive(Debug)]
661pub(crate) struct MediaIngress {
662 pub(crate) srtp: mpsc::Receiver<Datagram>,
663 pub(crate) srtcp: mpsc::Receiver<Datagram>,
664}
665
666#[cfg(feature = "dtls")]
668#[derive(Debug)]
669pub(crate) struct Runtime {
670 pub(crate) socket: Arc<UdpSocket>,
671 pub(crate) media: MediaIngress,
672 pub(crate) ice: crate::ice::driver::Handle,
673 pub(crate) ingress: Arc<StdMutex<ComponentIngress>>,
674 pub(crate) owner: tokio::task::JoinHandle<()>,
675 pub(crate) ice_owner: tokio::task::JoinHandle<()>,
676 pub(crate) stop: Arc<crate::session::Stop>,
677 pub(crate) profile_tasks: Arc<ProfileTasks>,
678}
679
680#[cfg(feature = "dtls")]
681const DTLS_QUEUE: usize = 64;
682#[cfg(feature = "dtls")]
683const SRTP_QUEUE: usize = 64;
684#[cfg(feature = "dtls")]
685const SRTCP_QUEUE: usize = 32;
686#[cfg(feature = "dtls")]
687const OUTBOUND_QUEUE: usize = 64;
688#[cfg(feature = "dtls")]
689const MAX_OUTBOUND_DATAGRAM: usize = 1200;
690
691#[cfg(feature = "dtls")]
693#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
694pub(crate) async fn prepare(
695 socket: Arc<UdpSocket>,
696 local: crate::ice::LocalDescription,
697 ice_generation: u64,
698 identity: crate::dtls::openssl::Identity,
699 role: crate::dtls::Role,
700 fingerprint: sipx_sdp::fingerprint::Fingerprint,
701 timeout: Duration,
702 stop: Arc<crate::session::Stop>,
703 discards: Arc<crate::counters::DiscardMeters>,
704) -> Result<(Runtime, crate::SrtpKeys), BrowserStartError> {
705 let (finished, result) = tokio::sync::oneshot::channel();
706 let profile_tasks = Arc::new(ProfileTasks::default());
707 let mut cancellation = StopOnDrop {
708 stop: Arc::clone(&stop),
709 armed: true,
710 };
711 tokio::spawn(async move {
712 let task_permit = profile_tasks.enter();
713 #[cfg(all(test, feature = "dtls"))]
714 let _activity = SupervisorActivity::new();
715 let outcome = prepare_inner(
716 socket,
717 local,
718 ice_generation,
719 identity,
720 role,
721 fingerprint,
722 timeout,
723 stop,
724 discards,
725 Arc::clone(&profile_tasks),
726 )
727 .await;
728 drop(task_permit);
731 if let Err(returned) = finished.send(outcome)
732 && let Ok((runtime, _keys)) = returned
733 {
734 cleanup_runtime(runtime).await;
735 }
736 });
737 let outcome = result.await.map_err(|_| {
738 BrowserStartError::Worker(
739 "browser-component supervisor stopped without a result".to_owned(),
740 )
741 })?;
742 cancellation.armed = false;
743 outcome
744}
745
746#[cfg(feature = "dtls")]
747struct StopOnDrop {
748 stop: Arc<crate::session::Stop>,
749 armed: bool,
750}
751
752#[cfg(feature = "dtls")]
753impl Drop for StopOnDrop {
754 fn drop(&mut self) {
755 if self.armed {
756 self.stop.stop();
757 }
758 }
759}
760
761#[cfg(feature = "dtls")]
762#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
763async fn prepare_inner(
764 socket: Arc<UdpSocket>,
765 local: crate::ice::LocalDescription,
766 ice_generation: u64,
767 identity: crate::dtls::openssl::Identity,
768 role: crate::dtls::Role,
769 fingerprint: sipx_sdp::fingerprint::Fingerprint,
770 timeout: Duration,
771 stop: Arc<crate::session::Stop>,
772 discards: Arc<crate::counters::DiscardMeters>,
773 profile_tasks: Arc<ProfileTasks>,
774) -> Result<(Runtime, crate::SrtpKeys), BrowserStartError> {
775 let placeholder = SocketAddr::new(
776 socket
777 .local_addr()
778 .map_err(|error| {
779 BrowserStartError::Adapter(format!("component has no local address: {error}"))
780 })?
781 .ip(),
782 0,
783 );
784 let destinations = crate::ice::driver::Destinations {
785 rtp: Arc::new(Mutex::new(placeholder)),
786 rtcp: Arc::new(Mutex::new(None)),
787 };
788 let (agent, pending) = local.into_driver_parts();
789 let peering = agent.peering().cloned();
790 let crate::ice::driver::OwnedDriver {
791 handle: ice,
792 task: ice_owner,
793 } = crate::ice::driver::spawn_owned(
794 agent,
795 pending,
796 vec![Arc::clone(&socket)],
797 destinations,
798 Arc::clone(&stop),
799 discards,
800 Arc::clone(&profile_tasks),
801 );
802
803 let ingress = Arc::new(StdMutex::new(ComponentIngress::new(ice_generation)));
804 let (dtls_in, dtls_rx) = std::sync::mpsc::sync_channel(DTLS_QUEUE);
805 let (dtls_out, dtls_writes) = mpsc::channel(OUTBOUND_QUEUE);
806 let (audio_packets, srtp_rx) = mpsc::channel(SRTP_QUEUE);
807 let (control_packets, srtcp_rx) = mpsc::channel(SRTCP_QUEUE);
808 let owner = tokio::spawn(profile_task(
809 Arc::clone(&profile_tasks),
810 owner_loop(
811 Arc::clone(&socket),
812 ice.clone(),
813 Arc::clone(&ingress),
814 peering,
815 dtls_in,
816 dtls_writes,
817 audio_packets,
818 control_packets,
819 Arc::clone(&stop),
820 ),
821 ));
822 let mut tasks = PreparingTasks {
823 stop: Arc::clone(&stop),
824 owner: Some(owner),
825 ice_owner: Some(ice_owner),
826 dtls: None,
827 armed: true,
828 };
829
830 let selected = match ice.wait_selected(ice_generation).await {
831 Ok(selected) => selected,
832 Err(crate::ice::driver::SelectionError::Failed) => {
833 tasks.cleanup().await;
834 return Err(BrowserStartError::IceFailed);
835 }
836 Err(crate::ice::driver::SelectionError::Stopped) => {
837 tasks.cleanup().await;
838 return Err(BrowserStartError::IceStopped);
839 }
840 };
841 let transition = {
842 let mut gate = lock_ingress(&ingress);
843 gate.nominate(selected)
844 .and_then(|()| gate.begin_dtls(selected.remote))
845 };
846 if let Err(error) = transition {
847 tasks.cleanup().await;
848 return Err(error.into());
849 }
850 #[cfg(all(test, feature = "dtls"))]
851 {
852 DTLS_HANDSHAKING.store(true, Ordering::SeqCst);
853 HANDSHAKE_STARTED.notify_waiters();
854 }
855
856 let adapter = DtlsAdapter {
857 inbound: dtls_rx,
858 outbound: dtls_out,
859 timeout: timeout.saturating_add(Duration::from_millis(250)),
860 };
861 let dtls_tasks = Arc::clone(&profile_tasks);
862 tasks.dtls = Some(tokio::task::spawn_blocking(move || {
863 let _permit = dtls_tasks.enter();
864 let mut handshake = crate::dtls::openssl::Session::with_io(adapter, &identity)
865 .map_err(|error| crate::dtls::Error::Dtls(error.to_string()))?;
866 crate::dtls::establish_verified(&mut handshake, role, Some(&fingerprint))
867 }));
868 let handshake = if let Some(worker) = tasks.dtls.as_mut() {
869 tokio::time::timeout(timeout, worker).await
870 } else {
871 tasks.cleanup().await;
872 return Err(BrowserStartError::Worker(
873 "DTLS worker was not retained".to_owned(),
874 ));
875 };
876 let verified = match handshake {
877 Err(_elapsed) => {
878 tasks.cleanup().await;
879 return Err(BrowserStartError::DtlsTimeout);
880 }
881 Ok(Err(error)) => {
882 tasks.dtls.take();
883 tasks.cleanup().await;
884 return Err(BrowserStartError::Worker(error.to_string()));
885 }
886 Ok(Ok(Err(error))) => {
887 tasks.dtls.take();
888 tasks.cleanup().await;
889 return Err(error.into());
890 }
891 Ok(Ok(Ok(verified))) => {
892 tasks.dtls.take();
893 verified
894 }
895 };
896
897 let installation = {
898 let mut gate = lock_ingress(&ingress);
899 gate.install_verified_keys(verified)
900 .and_then(|()| gate.start_media())
901 };
902 let keys = match installation {
903 Ok(keys) => keys,
904 Err(error) => {
905 tasks.cleanup().await;
906 return Err(error.into());
907 }
908 };
909 let owner = tasks.owner.take().ok_or(BrowserStartError::IceStopped)?;
910 let ice_owner = tasks
911 .ice_owner
912 .take()
913 .ok_or(BrowserStartError::IceStopped)?;
914 tasks.armed = false;
915 Ok((
916 Runtime {
917 socket,
918 media: MediaIngress {
919 srtp: srtp_rx,
920 srtcp: srtcp_rx,
921 },
922 ice,
923 ingress,
924 owner,
925 ice_owner,
926 stop,
927 profile_tasks,
928 },
929 keys,
930 ))
931}
932
933#[cfg(feature = "dtls")]
934async fn cleanup_runtime(runtime: Runtime) {
935 runtime.stop.stop();
936 lock_ingress(&runtime.ingress).close();
937 runtime.owner.abort();
938 runtime.ice_owner.abort();
939 let _ = runtime.owner.await;
942 let _ = runtime.ice_owner.await;
943}
944
945#[cfg(feature = "dtls")]
946struct PreparingTasks {
947 stop: Arc<crate::session::Stop>,
948 owner: Option<tokio::task::JoinHandle<()>>,
949 ice_owner: Option<tokio::task::JoinHandle<()>>,
950 dtls: Option<tokio::task::JoinHandle<Result<crate::dtls::VerifiedKeys, crate::dtls::Error>>>,
951 armed: bool,
952}
953
954#[cfg(feature = "dtls")]
955impl PreparingTasks {
956 async fn cleanup(&mut self) {
957 self.stop.stop();
958 if let Some(task) = &self.dtls {
959 task.abort();
960 }
961 if let Some(task) = self.dtls.take() {
962 let _ = task.await;
965 }
966 if let Some(task) = self.owner.take() {
967 let _ = task.await;
970 }
971 if let Some(task) = self.ice_owner.take() {
972 let _ = task.await;
975 }
976 self.armed = false;
977 }
978}
979
980#[cfg(feature = "dtls")]
981impl Drop for PreparingTasks {
982 fn drop(&mut self) {
983 if !self.armed {
984 return;
985 }
986 self.stop.stop();
987 if let Some(task) = &self.dtls {
988 task.abort();
989 }
990 if let Some(task) = &self.owner {
991 task.abort();
992 }
993 if let Some(task) = &self.ice_owner {
994 task.abort();
995 }
996 }
997}
998
999#[cfg(all(test, feature = "dtls"))]
1000struct SupervisorActivity;
1001
1002#[cfg(all(test, feature = "dtls"))]
1003impl SupervisorActivity {
1004 fn new() -> Self {
1005 ACTIVE_SUPERVISORS.fetch_add(1, Ordering::SeqCst);
1006 SUPERVISOR_CHANGED.notify_waiters();
1007 Self
1008 }
1009}
1010
1011#[cfg(all(test, feature = "dtls"))]
1012impl Drop for SupervisorActivity {
1013 fn drop(&mut self) {
1014 ACTIVE_SUPERVISORS.fetch_sub(1, Ordering::SeqCst);
1015 DTLS_HANDSHAKING.store(false, Ordering::SeqCst);
1016 SUPERVISOR_CHANGED.notify_waiters();
1017 }
1018}
1019
1020#[cfg(feature = "dtls")]
1021#[allow(clippy::too_many_arguments)]
1022async fn owner_loop(
1023 socket: Arc<UdpSocket>,
1024 ice: crate::ice::driver::Handle,
1025 ingress: Arc<StdMutex<ComponentIngress>>,
1026 peering: Option<crate::ice::stun::Peering>,
1027 dtls: std::sync::mpsc::SyncSender<Vec<u8>>,
1028 mut dtls_writes: mpsc::Receiver<Vec<u8>>,
1029 audio_packets: mpsc::Sender<Datagram>,
1030 control_packets: mpsc::Sender<Datagram>,
1031 stop: Arc<crate::session::Stop>,
1032) {
1033 let mut datagram = vec![0u8; MAX_DATAGRAM + 1];
1034 let mut dtls_writes_open = true;
1035 loop {
1036 let received = tokio::select! {
1037 () = stop.wait() => return,
1038 write = dtls_writes.recv(), if dtls_writes_open => {
1039 let Some(bytes) = write else {
1040 dtls_writes_open = false;
1041 continue;
1042 };
1043 let peer = lock_ingress(&ingress).snapshot().selected.map(|pair| pair.remote);
1044 if let Some(peer) = peer
1045 && socket.send_to(&bytes, peer).await.is_err()
1046 {
1047 return;
1048 }
1049 continue;
1050 }
1051 received = socket.recv_from(&mut datagram) => received,
1052 };
1053 let Ok((length, source)) = received else {
1054 return;
1055 };
1056 let bytes = datagram.get(..length).unwrap_or_default();
1057 let disposition = lock_ingress(&ingress).admit(source, bytes);
1058 let IngressDisposition::Accepted(class) = disposition else {
1059 continue;
1060 };
1061 if bytes.len() < minimum_length(class) {
1062 lock_ingress(&ingress).note_malformed(class);
1063 continue;
1064 }
1065 if class == IngressClass::Stun {
1066 let accepted = {
1067 let mut gate = lock_ingress(&ingress);
1068 account_stun(&mut gate, bytes, peering.as_ref())
1069 };
1070 if !accepted {
1071 continue;
1072 }
1073 }
1074 let admitted = match class {
1075 IngressClass::Stun => ice.datagram(source, crate::ice::LocalBase(0), bytes.to_vec()),
1076 IngressClass::Dtls => dtls.try_send(bytes.to_vec()).is_ok(),
1077 IngressClass::Srtp => audio_packets
1078 .try_send(Datagram {
1079 source,
1080 bytes: bytes.to_vec(),
1081 })
1082 .is_ok(),
1083 IngressClass::Srtcp => control_packets
1084 .try_send(Datagram {
1085 source,
1086 bytes: bytes.to_vec(),
1087 })
1088 .is_ok(),
1089 };
1090 if !admitted {
1091 lock_ingress(&ingress).note_queue_full(class);
1092 }
1093 }
1094}
1095
1096#[cfg(feature = "dtls")]
1097enum StunDrop {
1098 Malformed,
1099 AuthenticationFailed,
1100}
1101
1102#[cfg(feature = "dtls")]
1103fn validate_stun(
1104 bytes: &[u8],
1105 peering: Option<&crate::ice::stun::Peering>,
1106) -> Result<(), StunDrop> {
1107 use crate::ice::stun::{Class, Message};
1108
1109 let message = Message::decode(bytes).map_err(|_| StunDrop::Malformed)?;
1110 let peering = peering.ok_or(StunDrop::AuthenticationFailed)?;
1111 let authenticated = match message.class() {
1112 Class::Request => {
1113 message.username() == Some(peering.inbound_username().as_str())
1114 && message.verify_integrity(peering.inbound_key())
1115 }
1116 Class::Success | Class::Error => message.verify_integrity(peering.outbound_key()),
1117 Class::Indication => !message.has_integrity(),
1118 };
1119 if authenticated {
1120 Ok(())
1121 } else {
1122 Err(StunDrop::AuthenticationFailed)
1123 }
1124}
1125
1126#[cfg(feature = "dtls")]
1127fn account_stun(
1128 ingress: &mut ComponentIngress,
1129 bytes: &[u8],
1130 peering: Option<&crate::ice::stun::Peering>,
1131) -> bool {
1132 match validate_stun(bytes, peering) {
1133 Ok(()) => true,
1134 Err(StunDrop::Malformed) => {
1135 ingress.note_malformed(IngressClass::Stun);
1136 false
1137 }
1138 Err(StunDrop::AuthenticationFailed) => {
1139 ingress.note_authentication_failure(IngressClass::Stun);
1140 false
1141 }
1142 }
1143}
1144
1145#[cfg(all(test, feature = "dtls"))]
1146#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
1147mod tests {
1148 use super::*;
1149 use crate::ice::stun::{Peering, RoleAttribute, connectivity_check, new_transaction_id};
1150 use crate::ice::{Gathering, Negotiation, Timers};
1151 use crate::{Codec, Config, MediaPort};
1152 use sipx_sdp::RtcpMode;
1153 use sipx_sdp::ice::{Credentials, Priority};
1154
1155 fn credentials(ufrag: &str, password: &str) -> Credentials {
1156 Credentials::new(ufrag, password).expect("valid ICE credentials")
1157 }
1158
1159 fn gathering(ufrag: &str, offerer: bool) -> Gathering {
1160 let mut gathering =
1161 Gathering::new(credentials(ufrag, "browserPassword0123456789AB"), offerer);
1162 gathering.agent.timers = Timers {
1163 ta: Duration::from_millis(20),
1164 tn: Duration::from_millis(250),
1165 tr: Duration::from_millis(200),
1166 ..Timers::default()
1167 };
1168 gathering
1169 }
1170
1171 fn peer(local: &crate::ice::LocalDescription) -> Negotiation {
1172 Negotiation::Ice {
1173 credentials: local.credentials().clone(),
1174 candidates: local.candidates().to_vec(),
1175 lite: false,
1176 }
1177 }
1178
1179 fn config(remote: SocketAddr) -> Config {
1180 let mut config = Config::new(remote, Codec::Pcmu);
1181 config.rtcp_mode = RtcpMode::Mux;
1182 config.rtcp_interval = None;
1183 config
1184 }
1185
1186 async fn wait_for_supervisors(expected: usize) {
1187 let wait = async {
1188 loop {
1189 let notified = SUPERVISOR_CHANGED.notified();
1190 tokio::pin!(notified);
1191 notified.as_mut().enable();
1192 if ACTIVE_SUPERVISORS.load(Ordering::SeqCst) == expected {
1193 return;
1194 }
1195 notified.await;
1196 }
1197 };
1198 tokio::time::timeout(Duration::from_secs(2), wait)
1199 .await .expect("supervisor count reaches the expected value");
1201 }
1202
1203 async fn wait_for_handshake() {
1204 let wait = async {
1205 loop {
1206 let notified = HANDSHAKE_STARTED.notified();
1207 tokio::pin!(notified);
1208 notified.as_mut().enable();
1209 if DTLS_HANDSHAKING.load(Ordering::SeqCst) {
1210 return;
1211 }
1212 notified.await;
1213 }
1214 };
1215 tokio::time::timeout(Duration::from_secs(2), wait)
1216 .await .expect("DTLS handshaking begins");
1218 }
1219
1220 #[test]
1221 fn structurally_sized_bad_stun_is_counted_exactly_once() {
1222 let local = credentials("local1", "localPassword0123456789AB");
1223 let remote = credentials("remote", "remotePassword0123456789A");
1224 let peering = Peering::new(local.clone(), remote.clone());
1225 let mut ingress = ComponentIngress::new(0);
1226
1227 let bad_cookie = [0u8; 20];
1228 assert!(!account_stun(&mut ingress, &bad_cookie, Some(&peering)));
1229 assert_eq!(ingress.snapshot().counts.stun_malformed, 1);
1230 assert_eq!(ingress.snapshot().counts.total(), 1);
1231
1232 let forged_remote = Peering::new(remote, credentials("local1", "wrongPassword0123456789A"));
1233 let forged = connectivity_check(
1234 new_transaction_id(),
1235 &forged_remote,
1236 Priority::new(100).expect("priority"),
1237 RoleAttribute::Controlled { tiebreaker: 7 },
1238 )
1239 .expect("encoded check");
1240 assert!(!account_stun(&mut ingress, &forged, Some(&peering)));
1241 let counts = ingress.snapshot().counts;
1242 assert_eq!(counts.stun_authentication_failures, 1);
1243 assert_eq!(counts.stun_malformed, 1);
1244 assert_eq!(counts.total(), 2);
1245 }
1246
1247 #[test]
1248 fn every_bounded_ingress_queue_refusal_is_counted_once() {
1249 let mut ingress = ComponentIngress::new(0);
1250 for class in [
1251 IngressClass::Stun,
1252 IngressClass::Dtls,
1253 IngressClass::Srtp,
1254 IngressClass::Srtcp,
1255 ] {
1256 ingress.note_queue_full(class);
1257 }
1258 let counts = ingress.snapshot().counts;
1259 assert_eq!(counts.stun_queue_refusals, 1);
1260 assert_eq!(counts.dtls_queue_refusals, 1);
1261 assert_eq!(counts.srtp_queue_refusals, 1);
1262 assert_eq!(counts.srtcp_queue_refusals, 1);
1263 assert_eq!(counts.total(), 4);
1264 }
1265
1266 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
1267 async fn cancellation_reaps_ice_and_dtls_preparation_without_a_detached_receiver() {
1268 use crate::dtls::Role;
1269 use crate::dtls::openssl::Identity;
1270
1271 let alice_port = MediaPort::bind("127.0.0.1:0".parse().expect("address"))
1273 .await
1274 .expect("Alice port");
1275 let bob_port = MediaPort::bind("127.0.0.1:0".parse().expect("address"))
1276 .await
1277 .expect("Bob fixture port");
1278 let alice_addr = alice_port.local_addr();
1279 let alice_gathering = gathering("alice4", true);
1280 let bob_gathering = gathering("bob004", false);
1281 let (mut alice_ice, bob_ice) = tokio::join!(
1282 alice_port.gather_with_rtcp_mode(&alice_gathering, RtcpMode::Mux),
1283 bob_port.gather_with_rtcp_mode(&bob_gathering, RtcpMode::Mux),
1284 );
1285 assert!(alice_ice.accept(&peer(&bob_ice)));
1286 let identity = Identity::generate().expect("identity");
1287 let fingerprint = Identity::generate()
1288 .expect("peer identity")
1289 .fingerprint()
1290 .expect("peer fingerprint");
1291 let checking = tokio::spawn(alice_port.start_browser_audio(
1292 config(bob_port.local_addr()),
1293 alice_ice,
1294 0,
1295 identity,
1296 Role::Client,
1297 fingerprint,
1298 Duration::from_secs(5),
1299 ));
1300 wait_for_supervisors(1).await;
1301 assert!(!DTLS_HANDSHAKING.load(Ordering::SeqCst));
1302 checking.abort();
1303 let _ = checking.await;
1306 wait_for_supervisors(0).await;
1307 drop(bob_port);
1308 drop(
1309 UdpSocket::bind(alice_addr)
1310 .await
1311 .expect("ICE cancellation released the port"),
1312 );
1313
1314 let alice_port = MediaPort::bind("127.0.0.1:0".parse().expect("address"))
1316 .await
1317 .expect("Alice port");
1318 let bob_port = MediaPort::bind("127.0.0.1:0".parse().expect("address"))
1319 .await
1320 .expect("Bob port");
1321 let (alice_addr, bob_addr) = (alice_port.local_addr(), bob_port.local_addr());
1322 let alice_gathering = gathering("alice5", true);
1323 let bob_gathering = gathering("bob005", false);
1324 let (mut alice_ice, mut bob_ice) = tokio::join!(
1325 alice_port.gather_with_rtcp_mode(&alice_gathering, RtcpMode::Mux),
1326 bob_port.gather_with_rtcp_mode(&bob_gathering, RtcpMode::Mux),
1327 );
1328 assert!(alice_ice.accept(&peer(&bob_ice)));
1329 assert!(bob_ice.accept(&peer(&alice_ice)));
1330 let bob = bob_port
1331 .start_with_ice(config(alice_addr), bob_ice)
1332 .expect("ordinary ICE peer");
1333 let identity = Identity::generate().expect("identity");
1334 let fingerprint = Identity::generate()
1335 .expect("peer identity")
1336 .fingerprint()
1337 .expect("peer fingerprint");
1338 let handshaking = tokio::spawn(alice_port.start_browser_audio(
1339 config(bob_addr),
1340 alice_ice,
1341 0,
1342 identity,
1343 Role::Client,
1344 fingerprint,
1345 Duration::from_secs(5),
1346 ));
1347 wait_for_handshake().await;
1348 handshaking.abort();
1349 let _ = handshaking.await;
1352 wait_for_supervisors(0).await;
1353 drop(bob);
1354 drop(
1355 UdpSocket::bind(alice_addr)
1356 .await
1357 .expect("DTLS cancellation released the port"),
1358 );
1359 }
1360
1361 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
1362 async fn actual_spawn_lifetimes_stay_inside_the_phase_task_bound() {
1363 use crate::dtls::Role;
1364 use crate::dtls::openssl::Identity;
1365
1366 let alice_port = MediaPort::bind("127.0.0.1:0".parse().expect("address"))
1367 .await
1368 .expect("Alice port");
1369 let bob_port = MediaPort::bind("127.0.0.1:0".parse().expect("address"))
1370 .await
1371 .expect("Bob port");
1372 let (alice_addr, bob_addr) = (alice_port.local_addr(), bob_port.local_addr());
1373 let alice_gathering = gathering("alice6", true);
1374 let bob_gathering = gathering("bob006", false);
1375 let (mut alice_ice, mut bob_ice) = tokio::join!(
1376 alice_port.gather_with_rtcp_mode(&alice_gathering, RtcpMode::Mux),
1377 bob_port.gather_with_rtcp_mode(&bob_gathering, RtcpMode::Mux),
1378 );
1379 assert!(alice_ice.accept(&peer(&bob_ice)));
1380 assert!(bob_ice.accept(&peer(&alice_ice)));
1381 let alice_identity = Identity::generate().expect("Alice identity");
1382 let bob_identity = Identity::generate().expect("Bob identity");
1383 let alice_fingerprint = alice_identity.fingerprint().expect("Alice fingerprint");
1384 let bob_fingerprint = bob_identity.fingerprint().expect("Bob fingerprint");
1385 let mut alice_config = config(bob_addr);
1386 let mut bob_config = config(alice_addr);
1387 alice_config.rtcp_interval = Some(Duration::from_millis(50));
1388 bob_config.rtcp_interval = Some(Duration::from_millis(50));
1389
1390 let (alice, bob) = tokio::time::timeout(Duration::from_secs(8), async {
1391 tokio::join!(
1392 alice_port.start_browser_audio(
1393 alice_config,
1394 alice_ice,
1395 0,
1396 alice_identity,
1397 Role::Client,
1398 bob_fingerprint,
1399 Duration::from_secs(5),
1400 ),
1401 bob_port.start_browser_audio(
1402 bob_config,
1403 bob_ice,
1404 0,
1405 bob_identity,
1406 Role::Server,
1407 alice_fingerprint,
1408 Duration::from_secs(5),
1409 ),
1410 )
1411 })
1412 .await .expect("both components reach Running");
1414 let alice = alice.expect("Alice starts");
1415 let bob = bob.expect("Bob starts");
1416
1417 tokio::time::timeout(Duration::from_secs(1), async {
1418 loop {
1419 let alice_peak = alice.browser_task_counts().map_or(0, |(_, _, peak)| peak);
1420 let bob_peak = bob.browser_task_counts().map_or(0, |(_, _, peak)| peak);
1421 if alice_peak == MAX_PROFILE_TASKS && bob_peak == MAX_PROFILE_TASKS {
1422 break;
1423 }
1424 tokio::task::yield_now().await;
1425 }
1426 })
1427 .await .expect("running workers enter the task census");
1429 for session in [&alice, &bob] {
1430 let (preparing_peak, active, peak) =
1431 session.browser_task_counts().expect("browser task census");
1432 assert_eq!(preparing_peak, 4);
1433 assert_eq!(peak, MAX_PROFILE_TASKS);
1434 assert!(active <= MAX_PROFILE_TASKS);
1435 }
1436 let alice_tasks = alice.browser_task_probe().expect("Alice task probe");
1437 let bob_tasks = bob.browser_task_probe().expect("Bob task probe");
1438 drop(alice);
1439 drop(bob);
1440 tokio::time::timeout(Duration::from_secs(1), async {
1441 while alice_tasks.counts().0 != 0 || bob_tasks.counts().0 != 0 {
1442 tokio::task::yield_now().await;
1443 }
1444 })
1445 .await .expect("running-session cancellation leaves no profile task");
1447 drop(
1448 UdpSocket::bind(alice_addr)
1449 .await
1450 .expect("Alice running cancellation released its port"),
1451 );
1452 drop(
1453 UdpSocket::bind(bob_addr)
1454 .await
1455 .expect("Bob running cancellation released its port"),
1456 );
1457 }
1458}
1459
1460#[cfg(feature = "dtls")]
1461const fn minimum_length(class: IngressClass) -> usize {
1462 match class {
1463 IngressClass::Stun => 20,
1464 IngressClass::Dtls => 13,
1465 IngressClass::Srtp => 12,
1466 IngressClass::Srtcp => 8,
1467 }
1468}
1469
1470pub(crate) fn lock_ingress(
1471 ingress: &Arc<StdMutex<ComponentIngress>>,
1472) -> std::sync::MutexGuard<'_, ComponentIngress> {
1473 ingress
1474 .lock()
1475 .unwrap_or_else(std::sync::PoisonError::into_inner)
1476}
1477
1478#[cfg(feature = "dtls")]
1479struct DtlsAdapter {
1480 inbound: std::sync::mpsc::Receiver<Vec<u8>>,
1481 outbound: mpsc::Sender<Vec<u8>>,
1482 timeout: Duration,
1483}
1484
1485#[cfg(feature = "dtls")]
1486impl Read for DtlsAdapter {
1487 fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
1488 let datagram = self.inbound.recv_timeout(self.timeout).map_err(|error| {
1489 let kind = match error {
1490 std::sync::mpsc::RecvTimeoutError::Timeout => std::io::ErrorKind::TimedOut,
1491 std::sync::mpsc::RecvTimeoutError::Disconnected => {
1492 std::io::ErrorKind::UnexpectedEof
1493 }
1494 };
1495 std::io::Error::new(kind, error)
1496 })?;
1497 let destination = buffer.get_mut(..datagram.len()).ok_or_else(|| {
1498 std::io::Error::new(
1499 std::io::ErrorKind::InvalidData,
1500 "DTLS record exceeds the adapter read buffer",
1501 )
1502 })?;
1503 destination.copy_from_slice(&datagram);
1504 Ok(datagram.len())
1505 }
1506}
1507
1508#[cfg(feature = "dtls")]
1509impl Write for DtlsAdapter {
1510 fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> {
1511 if buffer.len() > MAX_OUTBOUND_DATAGRAM {
1512 return Err(std::io::Error::new(
1513 std::io::ErrorKind::InvalidData,
1514 "DTLS record exceeds the browser-component outbound bound",
1515 ));
1516 }
1517 self.outbound
1518 .blocking_send(buffer.to_vec())
1519 .map_err(|error| std::io::Error::new(std::io::ErrorKind::BrokenPipe, error))?;
1520 Ok(buffer.len())
1521 }
1522
1523 fn flush(&mut self) -> std::io::Result<()> {
1524 Ok(())
1525 }
1526}