Skip to content

Commit 62b3f22

Browse files
marten-seemanngaukas
authored andcommitted
fix handling of ACK frames serialized after CRYPTO frames (#4018)
1 parent fba8d78 commit 62b3f22

File tree

2 files changed

+37
-51
lines changed

2 files changed

+37
-51
lines changed

connection.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -724,20 +724,22 @@ func (s *connection) idleTimeoutStartTime() time.Time {
724724
}
725725

726726
func (s *connection) handleHandshakeComplete() error {
727-
s.handshakeComplete = true
728727
defer s.handshakeCtxCancel()
729728
// Once the handshake completes, we have derived 1-RTT keys.
730-
// There's no point in queueing undecryptable packets for later decryption any more.
729+
// There's no point in queueing undecryptable packets for later decryption anymore.
731730
s.undecryptablePackets = nil
732731

733732
s.connIDManager.SetHandshakeComplete()
734733
s.connIDGenerator.SetHandshakeComplete()
735734

735+
// The server applies transport parameters right away, but the client side has to wait for handshake completion.
736+
// During a 0-RTT connection, the client is only allowed to use the new transport parameters for 1-RTT packets.
736737
if s.perspective == protocol.PerspectiveClient {
737738
s.applyTransportParameters()
738739
return nil
739740
}
740741

742+
// All these only apply to the server side.
741743
if err := s.handleHandshakeConfirmed(); err != nil {
742744
return err
743745
}
@@ -1235,6 +1237,7 @@ func (s *connection) handleFrames(
12351237
if log != nil {
12361238
frames = make([]logging.Frame, 0, 4)
12371239
}
1240+
handshakeWasComplete := s.handshakeComplete
12381241
var handleErr error
12391242
for len(data) > 0 {
12401243
l, frame, err := s.frameParser.ParseNext(data, encLevel, s.version)
@@ -1271,6 +1274,17 @@ func (s *connection) handleFrames(
12711274
return false, handleErr
12721275
}
12731276
}
1277+
1278+
// Handle completion of the handshake after processing all the frames.
1279+
// This ensures that we correctly handle the following case on the server side:
1280+
// We receive a Handshake packet that contains the CRYPTO frame that allows us to complete the handshake,
1281+
// and an ACK serialized after that CRYPTO frame. In this case, we still want to process the ACK frame.
1282+
if !handshakeWasComplete && s.handshakeComplete {
1283+
if err := s.handleHandshakeComplete(); err != nil {
1284+
return false, err
1285+
}
1286+
}
1287+
12741288
return
12751289
}
12761290

@@ -1366,7 +1380,9 @@ func (s *connection) handleHandshakeEvents() error {
13661380
case handshake.EventNoEvent:
13671381
return nil
13681382
case handshake.EventHandshakeComplete:
1369-
err = s.handleHandshakeComplete()
1383+
// Don't call handleHandshakeComplete yet.
1384+
// It's advantageous to process ACK frames that might be serialized after the CRYPTO frame first.
1385+
s.handshakeComplete = true
13701386
case handshake.EventReceivedTransportParameters:
13711387
err = s.handleTransportParameters(ev.TransportParameters)
13721388
case handshake.EventRestoredTransportParameters:
@@ -1494,6 +1510,9 @@ func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.Encr
14941510
if !acked1RTTPacket {
14951511
return nil
14961512
}
1513+
// On the client side: If the packet acknowledged a 1-RTT packet, this confirms the handshake.
1514+
// This is only possible if the ACK was sent in a 1-RTT packet.
1515+
// This is an optimization over simply waiting for a HANDSHAKE_DONE frame, see section 4.1.2 of RFC 9001.
14971516
if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed {
14981517
if err := s.handleHandshakeConfirmed(); err != nil {
14991518
return err
@@ -1665,6 +1684,9 @@ func (s *connection) restoreTransportParameters(params *wire.TransportParameters
16651684
}
16661685

16671686
func (s *connection) handleTransportParameters(params *wire.TransportParameters) error {
1687+
if s.tracer != nil {
1688+
s.tracer.ReceivedTransportParameters(params)
1689+
}
16681690
if err := s.checkTransportParameters(params); err != nil {
16691691
return &qerr.TransportError{
16701692
ErrorCode: qerr.TransportParameterError,
@@ -1699,9 +1721,6 @@ func (s *connection) checkTransportParameters(params *wire.TransportParameters)
16991721
if s.logger.Debug() {
17001722
s.logger.Debugf("Processed Transport Parameters: %s", params)
17011723
}
1702-
if s.tracer != nil {
1703-
s.tracer.ReceivedTransportParameters(params)
1704-
}
17051724

17061725
// check the initial_source_connection_id
17071726
if params.InitialSourceConnectionID != s.handshakeDestConnID {

connection_test.go

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,7 +1892,6 @@ var _ = Describe("Connection", func() {
18921892

18931893
It("cancels the HandshakeComplete context when the handshake completes", func() {
18941894
packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes()
1895-
finishHandshake := make(chan struct{})
18961895
sph := mockackhandler.NewMockSentPacketHandler(mockCtrl)
18971896
conn.sentPacketHandler = sph
18981897
tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake)
@@ -1902,53 +1901,26 @@ var _ = Describe("Connection", func() {
19021901
sph.EXPECT().DropPackets(protocol.EncryptionHandshake)
19031902
sph.EXPECT().SetHandshakeConfirmed()
19041903
connRunner.EXPECT().Retire(clientDestConnID)
1905-
go func() {
1906-
defer GinkgoRecover()
1907-
<-finishHandshake
1908-
cryptoSetup.EXPECT().StartHandshake()
1909-
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete})
1910-
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent})
1911-
cryptoSetup.EXPECT().SetHandshakeConfirmed()
1912-
cryptoSetup.EXPECT().GetSessionTicket()
1913-
conn.run()
1914-
}()
1904+
cryptoSetup.EXPECT().SetHandshakeConfirmed()
1905+
cryptoSetup.EXPECT().GetSessionTicket()
19151906
handshakeCtx := conn.HandshakeComplete()
19161907
Consistently(handshakeCtx).ShouldNot(BeClosed())
1917-
close(finishHandshake)
1908+
Expect(conn.handleHandshakeComplete()).To(Succeed())
19181909
Eventually(handshakeCtx).Should(BeClosed())
1919-
// make sure the go routine returns
1920-
streamManager.EXPECT().CloseWithError(gomock.Any())
1921-
expectReplaceWithClosed()
1922-
packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
1923-
cryptoSetup.EXPECT().Close()
1924-
mconn.EXPECT().Write(gomock.Any(), gomock.Any())
1925-
tracer.EXPECT().ClosedConnection(gomock.Any())
1926-
tracer.EXPECT().Close()
1927-
conn.shutdown()
1928-
Eventually(conn.Context().Done()).Should(BeClosed())
19291910
})
19301911

19311912
It("sends a session ticket when the handshake completes", func() {
19321913
const size = protocol.MaxPostHandshakeCryptoFrameSize * 3 / 2
19331914
packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes()
1934-
finishHandshake := make(chan struct{})
19351915
connRunner.EXPECT().Retire(clientDestConnID)
19361916
conn.sentPacketHandler.DropPackets(protocol.EncryptionInitial)
19371917
tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake)
1938-
go func() {
1939-
defer GinkgoRecover()
1940-
<-finishHandshake
1941-
cryptoSetup.EXPECT().StartHandshake()
1942-
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete})
1943-
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent})
1944-
cryptoSetup.EXPECT().SetHandshakeConfirmed()
1945-
cryptoSetup.EXPECT().GetSessionTicket().Return(make([]byte, size), nil)
1946-
conn.run()
1947-
}()
1918+
cryptoSetup.EXPECT().SetHandshakeConfirmed()
1919+
cryptoSetup.EXPECT().GetSessionTicket().Return(make([]byte, size), nil)
19481920

19491921
handshakeCtx := conn.HandshakeComplete()
19501922
Consistently(handshakeCtx).ShouldNot(BeClosed())
1951-
close(finishHandshake)
1923+
Expect(conn.handleHandshakeComplete()).To(Succeed())
19521924
var frames []ackhandler.Frame
19531925
Eventually(func() []ackhandler.Frame {
19541926
frames, _ = conn.framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1)
@@ -1964,16 +1936,6 @@ var _ = Describe("Connection", func() {
19641936
}
19651937
}
19661938
Expect(size).To(BeEquivalentTo(s))
1967-
// make sure the go routine returns
1968-
streamManager.EXPECT().CloseWithError(gomock.Any())
1969-
expectReplaceWithClosed()
1970-
packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil)
1971-
cryptoSetup.EXPECT().Close()
1972-
mconn.EXPECT().Write(gomock.Any(), gomock.Any())
1973-
tracer.EXPECT().ClosedConnection(gomock.Any())
1974-
tracer.EXPECT().Close()
1975-
conn.shutdown()
1976-
Eventually(conn.Context().Done()).Should(BeClosed())
19771939
})
19781940

19791941
It("doesn't cancel the HandshakeComplete context when the handshake fails", func() {
@@ -2028,6 +1990,7 @@ var _ = Describe("Connection", func() {
20281990
cryptoSetup.EXPECT().SetHandshakeConfirmed()
20291991
cryptoSetup.EXPECT().GetSessionTicket()
20301992
mconn.EXPECT().Write(gomock.Any(), gomock.Any())
1993+
Expect(conn.handleHandshakeComplete()).To(Succeed())
20311994
conn.run()
20321995
}()
20331996
Eventually(done).Should(BeClosed())
@@ -2351,6 +2314,7 @@ var _ = Describe("Connection", func() {
23512314
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent})
23522315
cryptoSetup.EXPECT().GetSessionTicket().MaxTimes(1)
23532316
cryptoSetup.EXPECT().SetHandshakeConfirmed().MaxTimes(1)
2317+
Expect(conn.handleHandshakeComplete()).To(Succeed())
23542318
err := conn.run()
23552319
nerr, ok := err.(net.Error)
23562320
Expect(ok).To(BeTrue())
@@ -2868,7 +2832,10 @@ var _ = Describe("Client Connection", func() {
28682832
TransportParameters: params,
28692833
})
28702834
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete}).MaxTimes(1)
2871-
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}).MaxTimes(1)
2835+
cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}).MaxTimes(1).Do(func() {
2836+
defer GinkgoRecover()
2837+
Expect(conn.handleHandshakeComplete()).To(Succeed())
2838+
})
28722839
errChan <- conn.run()
28732840
close(errChan)
28742841
}()

0 commit comments

Comments
 (0)