Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

* Fix DTLS 1.2 ClientHello retransmissions #160

# 0.7.2

* Stop advertising unsupported RSA signatures in DTLS 1.2 CertificateRequest #157
Expand Down
9 changes: 7 additions & 2 deletions src/dtls12/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,13 @@ impl Engine {
}

match (self.connect_timeout, self.flight_timeout) {
// Keep this before the `(Armed, _)` arms. Starting a new flight resets its timer to
// `Unarmed`, but leaves the overall connection timer armed. If that mixed state
// returned the connection deadline, the caller would not drive `handle_timeout` to
// arm the flight timer until the whole handshake expired, so the flight would never
// be retransmitted. Returning `now` requests that immediate drive; the next poll sees
// both concrete deadlines and can return the earlier one.
(Timeout::Unarmed, _) | (_, Timeout::Unarmed) => now,
(Timeout::Armed(c), Timeout::Armed(f)) => {
if c < f {
c
Expand All @@ -523,8 +530,6 @@ impl Engine {
}
(Timeout::Armed(c), _) => c,
(_, Timeout::Armed(f)) => f,
// Both Unarmed or mixed Unarmed/Disabled: return current time
// to trigger handle_timeout on the next cycle.
_ => now,
}
}
Expand Down
44 changes: 44 additions & 0 deletions tests/dtls12/retransmit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,50 @@ fn first_record_matching(datagrams: &[Vec<u8>], content_type: u8, epoch: u16) ->
None
}

#[test]
#[cfg(feature = "rcgen")]
fn dtls12_client_hello_retransmits_using_advertised_deadlines() {
use dimpl::certificate::generate_self_signed_certificate;

let now = Instant::now();
let certificate = generate_self_signed_certificate().expect("generate client certificate");
let mut client = Dtls::new_12(Arc::new(Config::default()), certificate, now);
client.set_active(true);

client.handle_timeout(now).expect("start client handshake");
let initial_output = drain_outputs(&mut client);
assert!(
!initial_output.packets.is_empty(),
"client should emit ClientHello"
);

// Drop the initial flight and handle the timeout.
let arm_at = initial_output
.timeout
.expect("client should advertise a deadline");
client
.handle_timeout(arm_at)
.expect("arm ClientHello retransmission");
let after_timer_arming = drain_outputs(&mut client);
assert!(
after_timer_arming.packets.is_empty(),
"arming should not retransmit early"
);

let retransmit_at = after_timer_arming
.timeout
.expect("client should advertise a retransmission deadline");
client
.handle_timeout(retransmit_at)
.expect("retransmit ClientHello");
let retransmission_output = drain_outputs(&mut client);

assert!(
!retransmission_output.packets.is_empty(),
"the dropped ClientHello flight was not retransmitted"
);
}

#[test]
#[cfg(feature = "rcgen")]
fn dtls12_resends_each_flight_epoch_and_sequence_increase() {
Expand Down
Loading