Skip to content

Commit 5207a94

Browse files
kvasilyealgesten
andauthored
Handle DTLS close_notify shutdown (algesten#956)
* DTLS close_notify handling * Formatting * Assert on reciprocal close too * Fix formatting * When closing, only process DTLS messages and ignore media etc. * Formatting nitpick * Fix clippy issue * Remove special closing logic * A different approach to closing * Remove duplicate comment * Fix a clippy nag * Let's try to fix Windows tests failing * Add close_notify changelog entry --------- Co-authored-by: Martin Algesten <martin@algesten.se>
1 parent 05ba8d0 commit 5207a94

13 files changed

Lines changed: 241 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22

3+
* Send DTLS `close_notify` on `Rtc::close()` #956
34
* Fix `Simulcast::add_recv_layer` to push to recv instead of send #968
45
* Accept any non-empty SDP session name (`s=`), not just `s=-` (RFC 8866 section 5.3)
56

crates/proto/src/crypto/provider.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ pub trait DtlsInstance: CryptoSafe {
192192

193193
/// Return the negotiated DTLS protocol version. May return `None` before handshake completion.
194194
fn protocol_version(&self) -> Option<ProtocolVersion>;
195+
196+
/// Initiate graceful shutdown by sending a DTLS `close_notify` alert.
197+
fn close(&mut self) -> Result<(), DtlsImplError>;
195198
}
196199

197200
// ============================================================================

crypto/apple-crypto/src/dtls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ impl DtlsInstance for AppleCryptoDtlsInstance {
169169
fn protocol_version(&self) -> Option<ProtocolVersion> {
170170
self.dtls.protocol_version()
171171
}
172+
173+
fn close(&mut self) -> Result<(), DtlsImplError> {
174+
self.dtls.close()
175+
}
172176
}
173177

174178
#[cfg(test)]

crypto/aws-lc-rs/src/dtls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,8 @@ impl DtlsInstance for AwsLcRsDtlsInstance {
111111
fn protocol_version(&self) -> Option<ProtocolVersion> {
112112
self.dtls.protocol_version()
113113
}
114+
115+
fn close(&mut self) -> Result<(), DtlsImplError> {
116+
self.dtls.close()
117+
}
114118
}

crypto/openssl/src/dtls_dimpl.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,8 @@ impl DtlsInstance for DimplDtlsInstance {
112112
fn protocol_version(&self) -> Option<ProtocolVersion> {
113113
self.dtls.protocol_version()
114114
}
115+
116+
fn close(&mut self) -> Result<(), DtlsImplError> {
117+
self.dtls.close()
118+
}
115119
}

crypto/openssl/src/dtls_ossl.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,10 @@ pub(super) struct OsslDtlsInstance {
434434
queued_app_data: VecDeque<Vec<u8>>,
435435
next_timeout: Option<Instant>,
436436
connected_emitted: bool,
437+
/// Close handling
438+
close_notify_received: bool,
439+
close_notify_emitted: bool,
440+
close_notify_sent: bool,
437441
}
438442

439443
impl std::fmt::Debug for OsslDtlsInstance {
@@ -454,6 +458,9 @@ impl OsslDtlsInstance {
454458
queued_app_data: VecDeque::new(),
455459
next_timeout: None,
456460
connected_emitted: false,
461+
close_notify_received: false,
462+
close_notify_emitted: false,
463+
close_notify_sent: false,
457464
})
458465
}
459466

@@ -498,7 +505,30 @@ impl DtlsInstance for OsslDtlsInstance {
498505
fn handle_packet(&mut self, packet: &[u8]) -> Result<(), DtlsImplError> {
499506
match self.inner.handle_receive(packet) {
500507
Ok(Some(data)) => {
501-
self.pending_application_data.push_back(data);
508+
if data.is_empty() {
509+
// Zero-length read — confirm it's a close_notify via SSL_get_shutdown
510+
let is_shutdown =
511+
if let State::Established(ref mut stream) = self.inner.tls.state {
512+
stream
513+
.get_shutdown()
514+
.contains(openssl::ssl::ShutdownState::RECEIVED)
515+
} else {
516+
false
517+
};
518+
if is_shutdown {
519+
self.close_notify_received = true;
520+
// Send reciprocal close_notify per RFC 5246 §7.2.1,
521+
// but only if we haven't already sent one ourselves.
522+
if !self.close_notify_sent {
523+
if let State::Established(ref mut stream) = self.inner.tls.state {
524+
let _ = stream.shutdown();
525+
}
526+
self.close_notify_sent = true;
527+
}
528+
}
529+
} else {
530+
self.pending_application_data.push_back(data);
531+
}
502532
}
503533
Ok(None) => {}
504534
Err(e) => {
@@ -568,6 +598,12 @@ impl DtlsInstance for OsslDtlsInstance {
568598
}
569599
}
570600

601+
// Return close_notify event (after packets so reciprocal alert goes out first)
602+
if self.close_notify_received && !self.close_notify_emitted {
603+
self.close_notify_emitted = true;
604+
return DtlsOutput::CloseNotify;
605+
}
606+
571607
// Return application data
572608
if let Some(data) = self.pending_application_data.pop_front() {
573609
if data.len() <= buf.len() {
@@ -615,6 +651,15 @@ impl DtlsInstance for OsslDtlsInstance {
615651
fn protocol_version(&self) -> Option<ProtocolVersion> {
616652
Some(ProtocolVersion::DTLS1_2)
617653
}
654+
655+
fn close(&mut self) -> Result<(), DtlsImplError> {
656+
if let State::Established(ref mut stream) = self.inner.tls.state {
657+
let _ = stream.shutdown();
658+
self.close_notify_sent = true;
659+
self.collect_output();
660+
}
661+
Ok(())
662+
}
618663
}
619664

620665
// ============================================================================

crypto/rust-crypto/src/dtls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,8 @@ impl DtlsInstance for RustCryptoDtlsInstance {
111111
fn protocol_version(&self) -> Option<ProtocolVersion> {
112112
self.dtls.protocol_version()
113113
}
114+
115+
fn close(&mut self) -> Result<(), DtlsImplError> {
116+
self.dtls.close()
117+
}
114118
}

crypto/wincrypto/src/dtls_dimpl.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ impl DtlsInstance for WinCryptoDtlsInstance {
102102
fn protocol_version(&self) -> Option<ProtocolVersion> {
103103
self.dtls.protocol_version()
104104
}
105+
106+
fn close(&mut self) -> Result<(), DtlsImplError> {
107+
self.dtls.close()
108+
}
105109
}
106110

107111
// ============================================================================

crypto/wincrypto/src/dtls_schannel.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,10 @@ impl DtlsInstance for WinCryptoDtlsInstance {
252252
fn protocol_version(&self) -> Option<ProtocolVersion> {
253253
Some(ProtocolVersion::DTLS1_2)
254254
}
255+
256+
fn close(&mut self) -> Result<(), DtlsImplError> {
257+
Err(DtlsImplError::CryptoError(
258+
"SChannel native DTLS does not support close_notify".into(),
259+
))
260+
}
255261
}

crypto/wincrypto/src/sys/provider.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,4 +409,10 @@ impl DtlsInstance for WinCryptoDtlsInstance {
409409
fn is_active(&self) -> bool {
410410
self.dtls.is_client().unwrap_or(false)
411411
}
412+
413+
fn close(&mut self) -> Result<(), DtlsImplError> {
414+
Err(DtlsImplError::CryptoError(
415+
"SChannel native DTLS does not support close_notify".into(),
416+
))
417+
}
412418
}

0 commit comments

Comments
 (0)