mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
fix handling of ACK frames serialized after CRYPTO frames (#4018)
This commit is contained in:
parent
fba8d784a8
commit
62b3f22a77
2 changed files with 37 additions and 51 deletions
|
@ -724,20 +724,22 @@ func (s *connection) idleTimeoutStartTime() time.Time {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *connection) handleHandshakeComplete() error {
|
func (s *connection) handleHandshakeComplete() error {
|
||||||
s.handshakeComplete = true
|
|
||||||
defer s.handshakeCtxCancel()
|
defer s.handshakeCtxCancel()
|
||||||
// Once the handshake completes, we have derived 1-RTT keys.
|
// Once the handshake completes, we have derived 1-RTT keys.
|
||||||
// There's no point in queueing undecryptable packets for later decryption any more.
|
// There's no point in queueing undecryptable packets for later decryption anymore.
|
||||||
s.undecryptablePackets = nil
|
s.undecryptablePackets = nil
|
||||||
|
|
||||||
s.connIDManager.SetHandshakeComplete()
|
s.connIDManager.SetHandshakeComplete()
|
||||||
s.connIDGenerator.SetHandshakeComplete()
|
s.connIDGenerator.SetHandshakeComplete()
|
||||||
|
|
||||||
|
// The server applies transport parameters right away, but the client side has to wait for handshake completion.
|
||||||
|
// During a 0-RTT connection, the client is only allowed to use the new transport parameters for 1-RTT packets.
|
||||||
if s.perspective == protocol.PerspectiveClient {
|
if s.perspective == protocol.PerspectiveClient {
|
||||||
s.applyTransportParameters()
|
s.applyTransportParameters()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All these only apply to the server side.
|
||||||
if err := s.handleHandshakeConfirmed(); err != nil {
|
if err := s.handleHandshakeConfirmed(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1235,6 +1237,7 @@ func (s *connection) handleFrames(
|
||||||
if log != nil {
|
if log != nil {
|
||||||
frames = make([]logging.Frame, 0, 4)
|
frames = make([]logging.Frame, 0, 4)
|
||||||
}
|
}
|
||||||
|
handshakeWasComplete := s.handshakeComplete
|
||||||
var handleErr error
|
var handleErr error
|
||||||
for len(data) > 0 {
|
for len(data) > 0 {
|
||||||
l, frame, err := s.frameParser.ParseNext(data, encLevel, s.version)
|
l, frame, err := s.frameParser.ParseNext(data, encLevel, s.version)
|
||||||
|
@ -1271,6 +1274,17 @@ func (s *connection) handleFrames(
|
||||||
return false, handleErr
|
return false, handleErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle completion of the handshake after processing all the frames.
|
||||||
|
// This ensures that we correctly handle the following case on the server side:
|
||||||
|
// We receive a Handshake packet that contains the CRYPTO frame that allows us to complete the handshake,
|
||||||
|
// and an ACK serialized after that CRYPTO frame. In this case, we still want to process the ACK frame.
|
||||||
|
if !handshakeWasComplete && s.handshakeComplete {
|
||||||
|
if err := s.handleHandshakeComplete(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1366,7 +1380,9 @@ func (s *connection) handleHandshakeEvents() error {
|
||||||
case handshake.EventNoEvent:
|
case handshake.EventNoEvent:
|
||||||
return nil
|
return nil
|
||||||
case handshake.EventHandshakeComplete:
|
case handshake.EventHandshakeComplete:
|
||||||
err = s.handleHandshakeComplete()
|
// Don't call handleHandshakeComplete yet.
|
||||||
|
// It's advantageous to process ACK frames that might be serialized after the CRYPTO frame first.
|
||||||
|
s.handshakeComplete = true
|
||||||
case handshake.EventReceivedTransportParameters:
|
case handshake.EventReceivedTransportParameters:
|
||||||
err = s.handleTransportParameters(ev.TransportParameters)
|
err = s.handleTransportParameters(ev.TransportParameters)
|
||||||
case handshake.EventRestoredTransportParameters:
|
case handshake.EventRestoredTransportParameters:
|
||||||
|
@ -1494,6 +1510,9 @@ func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.Encr
|
||||||
if !acked1RTTPacket {
|
if !acked1RTTPacket {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// On the client side: If the packet acknowledged a 1-RTT packet, this confirms the handshake.
|
||||||
|
// This is only possible if the ACK was sent in a 1-RTT packet.
|
||||||
|
// This is an optimization over simply waiting for a HANDSHAKE_DONE frame, see section 4.1.2 of RFC 9001.
|
||||||
if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed {
|
if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed {
|
||||||
if err := s.handleHandshakeConfirmed(); err != nil {
|
if err := s.handleHandshakeConfirmed(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1665,6 +1684,9 @@ func (s *connection) restoreTransportParameters(params *wire.TransportParameters
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *connection) handleTransportParameters(params *wire.TransportParameters) error {
|
func (s *connection) handleTransportParameters(params *wire.TransportParameters) error {
|
||||||
|
if s.tracer != nil {
|
||||||
|
s.tracer.ReceivedTransportParameters(params)
|
||||||
|
}
|
||||||
if err := s.checkTransportParameters(params); err != nil {
|
if err := s.checkTransportParameters(params); err != nil {
|
||||||
return &qerr.TransportError{
|
return &qerr.TransportError{
|
||||||
ErrorCode: qerr.TransportParameterError,
|
ErrorCode: qerr.TransportParameterError,
|
||||||
|
@ -1699,9 +1721,6 @@ func (s *connection) checkTransportParameters(params *wire.TransportParameters)
|
||||||
if s.logger.Debug() {
|
if s.logger.Debug() {
|
||||||
s.logger.Debugf("Processed Transport Parameters: %s", params)
|
s.logger.Debugf("Processed Transport Parameters: %s", params)
|
||||||
}
|
}
|
||||||
if s.tracer != nil {
|
|
||||||
s.tracer.ReceivedTransportParameters(params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the initial_source_connection_id
|
// check the initial_source_connection_id
|
||||||
if params.InitialSourceConnectionID != s.handshakeDestConnID {
|
if params.InitialSourceConnectionID != s.handshakeDestConnID {
|
||||||
|
|
|
@ -1892,7 +1892,6 @@ var _ = Describe("Connection", func() {
|
||||||
|
|
||||||
It("cancels the HandshakeComplete context when the handshake completes", func() {
|
It("cancels the HandshakeComplete context when the handshake completes", func() {
|
||||||
packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes()
|
packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes()
|
||||||
finishHandshake := make(chan struct{})
|
|
||||||
sph := mockackhandler.NewMockSentPacketHandler(mockCtrl)
|
sph := mockackhandler.NewMockSentPacketHandler(mockCtrl)
|
||||||
conn.sentPacketHandler = sph
|
conn.sentPacketHandler = sph
|
||||||
tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake)
|
tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake)
|
||||||
|
@ -1902,53 +1901,26 @@ var _ = Describe("Connection", func() {
|
||||||
sph.EXPECT().DropPackets(protocol.EncryptionHandshake)
|
sph.EXPECT().DropPackets(protocol.EncryptionHandshake)
|
||||||
sph.EXPECT().SetHandshakeConfirmed()
|
sph.EXPECT().SetHandshakeConfirmed()
|
||||||
connRunner.EXPECT().Retire(clientDestConnID)
|
connRunner.EXPECT().Retire(clientDestConnID)
|
||||||
go func() {
|
cryptoSetup.EXPECT().SetHandshakeConfirmed()
|
||||||
defer GinkgoRecover()
|
cryptoSetup.EXPECT().GetSessionTicket()
|
||||||
<-finishHandshake
|
|
||||||
cryptoSetup.EXPECT().StartHandshake()
|
|
||||||
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete})
|
|
||||||
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent})
|
|
||||||
cryptoSetup.EXPECT().SetHandshakeConfirmed()
|
|
||||||
cryptoSetup.EXPECT().GetSessionTicket()
|
|
||||||
conn.run()
|
|
||||||
}()
|
|
||||||
handshakeCtx := conn.HandshakeComplete()
|
handshakeCtx := conn.HandshakeComplete()
|
||||||
Consistently(handshakeCtx).ShouldNot(BeClosed())
|
Consistently(handshakeCtx).ShouldNot(BeClosed())
|
||||||
close(finishHandshake)
|
Expect(conn.handleHandshakeComplete()).To(Succeed())
|
||||||
Eventually(handshakeCtx).Should(BeClosed())
|
Eventually(handshakeCtx).Should(BeClosed())
|
||||||
// make sure the go routine returns
|
|
||||||
streamManager.EXPECT().CloseWithError(gomock.Any())
|
|
||||||
expectReplaceWithClosed()
|
|
||||||
packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
|
|
||||||
cryptoSetup.EXPECT().Close()
|
|
||||||
mconn.EXPECT().Write(gomock.Any(), gomock.Any())
|
|
||||||
tracer.EXPECT().ClosedConnection(gomock.Any())
|
|
||||||
tracer.EXPECT().Close()
|
|
||||||
conn.shutdown()
|
|
||||||
Eventually(conn.Context().Done()).Should(BeClosed())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("sends a session ticket when the handshake completes", func() {
|
It("sends a session ticket when the handshake completes", func() {
|
||||||
const size = protocol.MaxPostHandshakeCryptoFrameSize * 3 / 2
|
const size = protocol.MaxPostHandshakeCryptoFrameSize * 3 / 2
|
||||||
packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes()
|
packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes()
|
||||||
finishHandshake := make(chan struct{})
|
|
||||||
connRunner.EXPECT().Retire(clientDestConnID)
|
connRunner.EXPECT().Retire(clientDestConnID)
|
||||||
conn.sentPacketHandler.DropPackets(protocol.EncryptionInitial)
|
conn.sentPacketHandler.DropPackets(protocol.EncryptionInitial)
|
||||||
tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake)
|
tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake)
|
||||||
go func() {
|
cryptoSetup.EXPECT().SetHandshakeConfirmed()
|
||||||
defer GinkgoRecover()
|
cryptoSetup.EXPECT().GetSessionTicket().Return(make([]byte, size), nil)
|
||||||
<-finishHandshake
|
|
||||||
cryptoSetup.EXPECT().StartHandshake()
|
|
||||||
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete})
|
|
||||||
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent})
|
|
||||||
cryptoSetup.EXPECT().SetHandshakeConfirmed()
|
|
||||||
cryptoSetup.EXPECT().GetSessionTicket().Return(make([]byte, size), nil)
|
|
||||||
conn.run()
|
|
||||||
}()
|
|
||||||
|
|
||||||
handshakeCtx := conn.HandshakeComplete()
|
handshakeCtx := conn.HandshakeComplete()
|
||||||
Consistently(handshakeCtx).ShouldNot(BeClosed())
|
Consistently(handshakeCtx).ShouldNot(BeClosed())
|
||||||
close(finishHandshake)
|
Expect(conn.handleHandshakeComplete()).To(Succeed())
|
||||||
var frames []ackhandler.Frame
|
var frames []ackhandler.Frame
|
||||||
Eventually(func() []ackhandler.Frame {
|
Eventually(func() []ackhandler.Frame {
|
||||||
frames, _ = conn.framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1)
|
frames, _ = conn.framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1)
|
||||||
|
@ -1964,16 +1936,6 @@ var _ = Describe("Connection", func() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(size).To(BeEquivalentTo(s))
|
Expect(size).To(BeEquivalentTo(s))
|
||||||
// make sure the go routine returns
|
|
||||||
streamManager.EXPECT().CloseWithError(gomock.Any())
|
|
||||||
expectReplaceWithClosed()
|
|
||||||
packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
|
|
||||||
cryptoSetup.EXPECT().Close()
|
|
||||||
mconn.EXPECT().Write(gomock.Any(), gomock.Any())
|
|
||||||
tracer.EXPECT().ClosedConnection(gomock.Any())
|
|
||||||
tracer.EXPECT().Close()
|
|
||||||
conn.shutdown()
|
|
||||||
Eventually(conn.Context().Done()).Should(BeClosed())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("doesn't cancel the HandshakeComplete context when the handshake fails", func() {
|
It("doesn't cancel the HandshakeComplete context when the handshake fails", func() {
|
||||||
|
@ -2028,6 +1990,7 @@ var _ = Describe("Connection", func() {
|
||||||
cryptoSetup.EXPECT().SetHandshakeConfirmed()
|
cryptoSetup.EXPECT().SetHandshakeConfirmed()
|
||||||
cryptoSetup.EXPECT().GetSessionTicket()
|
cryptoSetup.EXPECT().GetSessionTicket()
|
||||||
mconn.EXPECT().Write(gomock.Any(), gomock.Any())
|
mconn.EXPECT().Write(gomock.Any(), gomock.Any())
|
||||||
|
Expect(conn.handleHandshakeComplete()).To(Succeed())
|
||||||
conn.run()
|
conn.run()
|
||||||
}()
|
}()
|
||||||
Eventually(done).Should(BeClosed())
|
Eventually(done).Should(BeClosed())
|
||||||
|
@ -2351,6 +2314,7 @@ var _ = Describe("Connection", func() {
|
||||||
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent})
|
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent})
|
||||||
cryptoSetup.EXPECT().GetSessionTicket().MaxTimes(1)
|
cryptoSetup.EXPECT().GetSessionTicket().MaxTimes(1)
|
||||||
cryptoSetup.EXPECT().SetHandshakeConfirmed().MaxTimes(1)
|
cryptoSetup.EXPECT().SetHandshakeConfirmed().MaxTimes(1)
|
||||||
|
Expect(conn.handleHandshakeComplete()).To(Succeed())
|
||||||
err := conn.run()
|
err := conn.run()
|
||||||
nerr, ok := err.(net.Error)
|
nerr, ok := err.(net.Error)
|
||||||
Expect(ok).To(BeTrue())
|
Expect(ok).To(BeTrue())
|
||||||
|
@ -2868,7 +2832,10 @@ var _ = Describe("Client Connection", func() {
|
||||||
TransportParameters: params,
|
TransportParameters: params,
|
||||||
})
|
})
|
||||||
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete}).MaxTimes(1)
|
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete}).MaxTimes(1)
|
||||||
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}).MaxTimes(1)
|
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}).MaxTimes(1).Do(func() {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
Expect(conn.handleHandshakeComplete()).To(Succeed())
|
||||||
|
})
|
||||||
errChan <- conn.run()
|
errChan <- conn.run()
|
||||||
close(errChan)
|
close(errChan)
|
||||||
}()
|
}()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue