Skip to content
File

Blob: firmware/vendor/str0m/tests/dtls-close.rs

rust187 lines
1//! Tests for the Rtc shutdown states.
2#![cfg(any(
3 feature = "aws-lc-rs",
4 feature = "rust-crypto",
5 feature = "openssl",
6 feature = "openssl-dimpl",
7 feature = "wincrypto-dimpl",
8 feature = "apple-crypto",
9))]
10 
11use std::net::Ipv4Addr;
12use std::time::Instant;
13 
14use str0m::media::MediaKind;
15use str0m::rtp::rtcp::Rtcp;
16use str0m::rtp::{RawPacket, Ssrc};
17use str0m::{Candidate, Event, Input, Output, Rtc, RtcConfig, RtcError};
18use tracing::info_span;
19 
20mod common;
21use common::{TestRtc, init_crypto_default, init_log, progress};
22 
23fn direct_pair() -> (TestRtc, TestRtc) {
24 direct_pair_with_config(|c| c)
25}
26 
27fn direct_pair_with_config(configure: impl Fn(RtcConfig) -> RtcConfig) -> (TestRtc, TestRtc) {
28 init_log();
29 init_crypto_default();
30 
31 let now = Instant::now();
32 let mut l = TestRtc::new_with_rtc(info_span!("L"), configure(Rtc::builder()).build(now));
33 let mut r = TestRtc::new_with_rtc(info_span!("R"), configure(Rtc::builder()).build(now));
34 
35 let host_l = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp").unwrap();
36 let host_r = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp").unwrap();
37 l.add_local_candidate(host_l.clone());
38 l.add_remote_candidate(host_r.clone());
39 r.add_local_candidate(host_r);
40 r.add_remote_candidate(host_l);
41 
42 let finger_l = l.direct_api().local_dtls_fingerprint().clone();
43 let finger_r = r.direct_api().local_dtls_fingerprint().clone();
44 l.direct_api().set_remote_fingerprint(finger_r);
45 r.direct_api().set_remote_fingerprint(finger_l);
46 
47 let creds_l = l.direct_api().local_ice_credentials();
48 let creds_r = r.direct_api().local_ice_credentials();
49 l.direct_api().set_remote_ice_credentials(creds_r);
50 r.direct_api().set_remote_ice_credentials(creds_l);
51 
52 l.direct_api().set_ice_controlling(true);
53 r.direct_api().set_ice_controlling(false);
54 
55 l.direct_api().start_dtls(true).unwrap();
56 r.direct_api().start_dtls(false).unwrap();
57 l.direct_api().start_sctp(true);
58 r.direct_api().start_sctp(false);
59 
60 progress_until(&mut l, &mut r, "DTLS handshake", |l, r| {
61 l.is_connected() && r.is_connected()
62 });
63 
64 (l, r)
65}
66 
67fn progress_until(
68 l: &mut TestRtc,
69 r: &mut TestRtc,
70 label: &str,
71 mut done: impl FnMut(&TestRtc, &TestRtc) -> bool,
72) {
73 for _ in 0..500 {
74 if done(l, r) {
75 return;
76 }
77 progress(l, r).unwrap();
78 }
79 
80 panic!("{label} did not complete");
81}
82 
83fn next_transmit(rtc: &mut TestRtc) -> str0m::net::Transmit {
84 for _ in 0..100 {
85 match rtc.rtc.poll_output().unwrap() {
86 Output::Transmit(transmit) => return transmit,
87 Output::Event(event) => rtc.events.push((rtc.last, event)),
88 Output::Timeout(_) => {}
89 }
90 }
91 
92 panic!("expected transmit");
93}
94 
95fn deliver(transmit: &str0m::net::Transmit, rtc: &mut TestRtc) -> Result<(), RtcError> {
96 rtc.rtc
97 .handle_input(Input::Receive(rtc.last, transmit.try_into()?))
98}
99 
100fn deliver_until_closed_event(
101 sender: &mut TestRtc,
102 receiver: &mut TestRtc,
103) -> Result<(), RtcError> {
104 for _ in 0..100 {
105 let transmit = next_transmit(sender);
106 deliver(&transmit, receiver)?;
107 
108 for _ in 0..100 {
109 match receiver.rtc.poll_output()? {
110 Output::Event(Event::Closed) => return Ok(()),
111 Output::Event(event) => receiver.events.push((receiver.last, event)),
112 Output::Transmit(_) => {}
113 Output::Timeout(_) => break,
114 }
115 }
116 }
117 
118 panic!("expected remote closed event");
119}
120 
121#[test]
122fn local_dtls_close_drains_before_rtc_closes() -> Result<(), RtcError> {
123 let (mut l, mut r) = direct_pair();
124 
125 l.rtc.close()?;
126 
127 assert!(
128 l.rtc.is_alive(),
129 "local close_notify still needs to be emitted"
130 );
131 
132 progress_until(&mut l, &mut r, "local DTLS close", |l, r| {
133 !l.rtc.is_alive()
134 && r.events
135 .iter()
136 .any(|(_, event)| matches!(event, Event::Closed))
137 });
138 
139 assert!(!l.rtc.is_alive());
140 
141 Ok(())
142}
143 
144#[test]
145fn remote_dtls_close_auto_replies_before_rtc_closes() -> Result<(), RtcError> {
146 let (mut l, mut r) = direct_pair();
147 
148 l.rtc.close()?;
149 deliver_until_closed_event(&mut l, &mut r)?;
150 
151 assert!(
152 r.rtc.is_alive(),
153 "remote close_notify should leave Rtc alive until the response close_notify drains"
154 );
155 
156 progress_until(&mut l, &mut r, "remote DTLS close reply", |_, r| {
157 !r.rtc.is_alive()
158 });
159 
160 assert!(!r.rtc.is_alive());
161 
162 Ok(())
163}
164 
165#[test]
166fn close_sends_rtcp_bye_for_local_senders() -> Result<(), RtcError> {
167 let (mut l, mut r) = direct_pair_with_config(|c| c.enable_raw_packets(true));
168 let mid = "aud".into();
169 let ssrc: Ssrc = 42.into();
170 
171 l.direct_api().declare_media(mid, MediaKind::Audio);
172 l.direct_api().declare_stream_tx(ssrc, None, mid, None);
173 
174 l.rtc.close()?;
175 
176 progress_until(&mut l, &mut r, "RTCP BYE", |l, _| {
177 l.events.iter().any(|(_, event)| {
178 matches!(
179 event.as_raw_packet(),
180 Some(RawPacket::RtcpTx(Rtcp::Goodbye(bye))) if bye.reports.iter().any(|s| *s == ssrc)
181 )
182 })
183 });
184 
185 Ok(())
186}