-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Exchange TLS handshake data in CRYPTO frames. Receive packet protection keys from the TLS layer. Discard packet protection keys as the handshake progresses. Send and receive HANDSHAKE_DONE frames (used by the server to inform the client of the handshake completing). Add a very minimal implementation of CONNECTION_CLOSE, just enough to let us write tests that trigger immediate close of connections. For golang/go#58547 Change-Id: I77496ca65bd72977565733739d563eaa2bb7d8d3 Reviewed-on: https://go-review.googlesource.com/c/net/+/510915 Reviewed-by: Jonathan Amsterdam <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Damien Neil <[email protected]> Auto-Submit: Damien Neil <[email protected]>
- Loading branch information
Showing
13 changed files
with
1,105 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build go1.21 | ||
|
||
package quic | ||
|
||
import ( | ||
"crypto/tls" | ||
) | ||
|
||
// A Config structure configures a QUIC endpoint. | ||
// A Config must not be modified after it has been passed to a QUIC function. | ||
// A Config may be reused; the quic package will also not modify it. | ||
type Config struct { | ||
// TLSConfig is the endpoint's TLS configuration. | ||
// It must be non-nil and include at least one certificate or else set GetCertificate. | ||
TLSConfig *tls.Config | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build go1.21 | ||
|
||
package quic | ||
|
||
import ( | ||
"crypto/tls" | ||
"testing" | ||
) | ||
|
||
// Frames may be retransmitted either when the packet containing the frame is lost, or on PTO. | ||
// lostFrameTest runs a test in both configurations. | ||
func lostFrameTest(t *testing.T, f func(t *testing.T, pto bool)) { | ||
t.Run("lost", func(t *testing.T) { | ||
f(t, false) | ||
}) | ||
t.Run("pto", func(t *testing.T) { | ||
f(t, true) | ||
}) | ||
} | ||
|
||
// triggerLossOrPTO causes the conn to declare the last sent packet lost, | ||
// or advances to the PTO timer. | ||
func (tc *testConn) triggerLossOrPTO(ptype packetType, pto bool) { | ||
tc.t.Helper() | ||
if pto { | ||
if !tc.conn.loss.ptoTimerArmed { | ||
tc.t.Fatalf("PTO timer not armed, expected it to be") | ||
} | ||
tc.advanceTo(tc.conn.loss.timer) | ||
return | ||
} | ||
defer func(ignoreFrames map[byte]bool) { | ||
tc.ignoreFrames = ignoreFrames | ||
}(tc.ignoreFrames) | ||
tc.ignoreFrames = map[byte]bool{ | ||
frameTypeAck: true, | ||
frameTypePadding: true, | ||
} | ||
// Send three packets containing PINGs, and then respond with an ACK for the | ||
// last one. This puts the last packet before the PINGs outside the packet | ||
// reordering threshold, and it will be declared lost. | ||
const lossThreshold = 3 | ||
var num packetNumber | ||
for i := 0; i < lossThreshold; i++ { | ||
tc.conn.ping(spaceForPacketType(ptype)) | ||
d := tc.readDatagram() | ||
if d == nil { | ||
tc.t.Fatalf("conn is idle; want PING frame") | ||
} | ||
if d.packets[0].ptype != ptype { | ||
tc.t.Fatalf("conn sent %v packet; want %v", d.packets[0].ptype, ptype) | ||
} | ||
num = d.packets[0].num | ||
} | ||
tc.writeFrames(ptype, debugFrameAck{ | ||
ranges: []i64range[packetNumber]{ | ||
{num, num + 1}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestLostCRYPTOFrame(t *testing.T) { | ||
// "Data sent in CRYPTO frames is retransmitted [...] until all data has been acknowledged." | ||
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.1 | ||
lostFrameTest(t, func(t *testing.T, pto bool) { | ||
tc := newTestConn(t, clientSide) | ||
tc.ignoreFrame(frameTypeAck) | ||
|
||
tc.wantFrame("client sends Initial CRYPTO frame", | ||
packetTypeInitial, debugFrameCrypto{ | ||
data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial], | ||
}) | ||
tc.triggerLossOrPTO(packetTypeInitial, pto) | ||
tc.wantFrame("client resends Initial CRYPTO frame", | ||
packetTypeInitial, debugFrameCrypto{ | ||
data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial], | ||
}) | ||
|
||
tc.writeFrames(packetTypeInitial, | ||
debugFrameCrypto{ | ||
data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial], | ||
}) | ||
tc.writeFrames(packetTypeHandshake, | ||
debugFrameCrypto{ | ||
data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake], | ||
}) | ||
|
||
tc.wantFrame("client sends Handshake CRYPTO frame", | ||
packetTypeHandshake, debugFrameCrypto{ | ||
data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake], | ||
}) | ||
tc.triggerLossOrPTO(packetTypeHandshake, pto) | ||
tc.wantFrame("client resends Handshake CRYPTO frame", | ||
packetTypeHandshake, debugFrameCrypto{ | ||
data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake], | ||
}) | ||
}) | ||
} | ||
|
||
func TestLostHandshakeDoneFrame(t *testing.T) { | ||
// "The HANDSHAKE_DONE frame MUST be retransmitted until it is acknowledged." | ||
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.16 | ||
lostFrameTest(t, func(t *testing.T, pto bool) { | ||
tc := newTestConn(t, serverSide) | ||
tc.ignoreFrame(frameTypeAck) | ||
|
||
tc.writeFrames(packetTypeInitial, | ||
debugFrameCrypto{ | ||
data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial], | ||
}) | ||
tc.wantFrame("server sends Initial CRYPTO frame", | ||
packetTypeInitial, debugFrameCrypto{ | ||
data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial], | ||
}) | ||
tc.wantFrame("server sends Handshake CRYPTO frame", | ||
packetTypeHandshake, debugFrameCrypto{ | ||
data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake], | ||
}) | ||
tc.writeFrames(packetTypeHandshake, | ||
debugFrameCrypto{ | ||
data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake], | ||
}) | ||
|
||
tc.wantFrame("server sends HANDSHAKE_DONE after handshake completes", | ||
packetType1RTT, debugFrameHandshakeDone{}) | ||
tc.wantFrame("server sends session ticket in CRYPTO frame", | ||
packetType1RTT, debugFrameCrypto{ | ||
data: tc.cryptoDataOut[tls.QUICEncryptionLevelApplication], | ||
}) | ||
|
||
tc.triggerLossOrPTO(packetType1RTT, pto) | ||
tc.wantFrame("server resends HANDSHAKE_DONE", | ||
packetType1RTT, debugFrameHandshakeDone{}) | ||
tc.wantFrame("server resends session ticket", | ||
packetType1RTT, debugFrameCrypto{ | ||
data: tc.cryptoDataOut[tls.QUICEncryptionLevelApplication], | ||
}) | ||
}) | ||
} |
Oops, something went wrong.