diff --git a/quiche/src/lib.rs b/quiche/src/lib.rs index b505906f26..9a16486c95 100644 --- a/quiche/src/lib.rs +++ b/quiche/src/lib.rs @@ -1371,6 +1371,9 @@ where /// Total number of received packets. recv_count: usize, + /// Whether at least one packet from the peer was authenticated. + received_authenticated_packet: bool, + /// Total number of sent packets. sent_count: usize, @@ -2088,6 +2091,7 @@ impl Connection { application_protos: config.application_protos.clone(), recv_count: 0, + received_authenticated_packet: false, sent_count: 0, lost_count: 0, spurious_lost_count: 0, @@ -3325,6 +3329,8 @@ impl Connection { return Err(Error::Done); } + self.received_authenticated_packet = true; + // Packets with no frames are invalid. if payload.cap() == 0 { return Err(Error::InvalidPacket); @@ -7550,8 +7556,8 @@ impl Connection { }); } - // When no packet was successfully processed close connection immediately. - if self.recv_count == 0 { + // When no peer packet was authenticated, close connection immediately. + if self.recv_count == 0 && !self.received_authenticated_packet { self.mark_closed(); } @@ -8141,6 +8147,15 @@ impl Connection { if !self.handshake_confirmed { match epoch { + // When a first-flight error happens after packet + // authentication, prefer an Initial close that the peer + // can decrypt. + packet::Epoch::Application + if self.recv_count == 0 && + self.received_authenticated_packet && + self.crypto_ctx[packet::Epoch::Initial].has_keys() => + return Ok(Type::Initial), + // Downgrade the epoch to Handshake as the handshake is not // completed yet. packet::Epoch::Application => return Ok(Type::Handshake), diff --git a/quiche/src/tests.rs b/quiche/src/tests.rs index d58e817a61..ef548ccb83 100644 --- a/quiche/src/tests.rs +++ b/quiche/src/tests.rs @@ -385,6 +385,37 @@ fn missing_initial_source_connection_id( pipe.server_recv(&mut buf[..len]), Err(Error::InvalidTransportParam) ); + + assert_eq!( + pipe.server.local_error(), + Some(&ConnectionError { + is_app: false, + error_code: WireErrorCode::TransportParameterError as u64, + reason: vec![], + }) + ); + + assert!(!pipe.server.is_closed()); + + let (len, _) = pipe.server.send(&mut buf).unwrap(); + + let hdr = Header::from_slice(&mut buf[..len], pipe.client.source_id().len()) + .unwrap(); + assert_eq!(hdr.ty, Type::Initial); + + assert_eq!( + pipe.client_recv(&mut buf[..len]), + Ok(len), + ); + + assert_eq!( + pipe.client.peer_error(), + Some(&ConnectionError { + is_app: false, + error_code: WireErrorCode::TransportParameterError as u64, + reason: vec![], + }) + ); } #[rstest]