mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
crypto/tls: add QUICErrorEvent
Add a new QUICEvent type for reporting errors. This provides a way to report errors that don't occur as a result of QUICConn.Start, QUICConn.HandleData, or QUICConn.SendSessionTicket. Fixes #75108 Change-Id: I941371a21f26b940e75287a66d7e0211fc0baab1 Reviewed-on: https://go-review.googlesource.com/c/go/+/719040 Auto-Submit: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Roland Shoemaker <roland@golang.org>
This commit is contained in:
parent
3ad2e113fc
commit
bd2b117c2c
4 changed files with 73 additions and 0 deletions
3
api/next/75108.txt
Normal file
3
api/next/75108.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pkg crypto/tls, const QUICErrorEvent = 10 #75108
|
||||
pkg crypto/tls, const QUICErrorEvent QUICEventKind #75108
|
||||
pkg crypto/tls, type QUICEvent struct, Err error #75108
|
||||
2
doc/next/6-stdlib/99-minor/crypto/tls/75108.md
Normal file
2
doc/next/6-stdlib/99-minor/crypto/tls/75108.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
The [QUICConn] type used by QUIC implementations includes new event
|
||||
for reporting TLS handshake errors.
|
||||
|
|
@ -117,6 +117,11 @@ const (
|
|||
// The application may modify the [SessionState] before storing it.
|
||||
// This event only occurs on client connections.
|
||||
QUICStoreSession
|
||||
|
||||
// QUICErrorEvent indicates that a fatal error has occurred.
|
||||
// The handshake cannot proceed and the connection must be closed.
|
||||
// QUICEvent.Err is set.
|
||||
QUICErrorEvent
|
||||
)
|
||||
|
||||
// A QUICEvent is an event occurring on a QUIC connection.
|
||||
|
|
@ -138,6 +143,10 @@ type QUICEvent struct {
|
|||
|
||||
// Set for QUICResumeSession and QUICStoreSession.
|
||||
SessionState *SessionState
|
||||
|
||||
// Set for QUICErrorEvent.
|
||||
// The error will wrap AlertError.
|
||||
Err error
|
||||
}
|
||||
|
||||
type quicState struct {
|
||||
|
|
@ -157,6 +166,7 @@ type quicState struct {
|
|||
cancel context.CancelFunc
|
||||
|
||||
waitingForDrain bool
|
||||
errorReturned bool
|
||||
|
||||
// readbuf is shared between HandleData and the handshake goroutine.
|
||||
// HandshakeCryptoData passes ownership to the handshake goroutine by
|
||||
|
|
@ -229,6 +239,15 @@ func (q *QUICConn) NextEvent() QUICEvent {
|
|||
<-qs.signalc
|
||||
<-qs.blockedc
|
||||
}
|
||||
if err := q.conn.handshakeErr; err != nil {
|
||||
if qs.errorReturned {
|
||||
return QUICEvent{Kind: QUICNoEvent}
|
||||
}
|
||||
qs.errorReturned = true
|
||||
qs.events = nil
|
||||
qs.nextEvent = 0
|
||||
return QUICEvent{Kind: QUICErrorEvent, Err: q.conn.handshakeErr}
|
||||
}
|
||||
if qs.nextEvent >= len(qs.events) {
|
||||
qs.events = qs.events[:0]
|
||||
qs.nextEvent = 0
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -21,6 +22,7 @@ type testQUICConn struct {
|
|||
ticketOpts QUICSessionTicketOptions
|
||||
onResumeSession func(*SessionState)
|
||||
gotParams []byte
|
||||
gotError error
|
||||
earlyDataRejected bool
|
||||
complete bool
|
||||
}
|
||||
|
|
@ -109,6 +111,9 @@ func runTestQUICConnection(ctx context.Context, cli, srv *testQUICConn, onEvent
|
|||
if onEvent != nil && onEvent(e, a, b) {
|
||||
continue
|
||||
}
|
||||
if a.gotError != nil && e.Kind != QUICNoEvent {
|
||||
return fmt.Errorf("unexpected event %v after QUICErrorEvent", e.Kind)
|
||||
}
|
||||
switch e.Kind {
|
||||
case QUICNoEvent:
|
||||
idleCount++
|
||||
|
|
@ -152,6 +157,11 @@ func runTestQUICConnection(ctx context.Context, cli, srv *testQUICConn, onEvent
|
|||
}
|
||||
case QUICRejectedEarlyData:
|
||||
a.earlyDataRejected = true
|
||||
case QUICErrorEvent:
|
||||
if e.Err == nil {
|
||||
return errors.New("unexpected QUICErrorEvent with no Err")
|
||||
}
|
||||
a.gotError = e.Err
|
||||
}
|
||||
if e.Kind != QUICNoEvent {
|
||||
idleCount = 0
|
||||
|
|
@ -371,6 +381,45 @@ func TestQUICHandshakeError(t *testing.T) {
|
|||
if _, ok := errors.AsType[*CertificateVerificationError](err); !ok {
|
||||
t.Errorf("connection handshake terminated with error %q, want CertificateVerificationError", err)
|
||||
}
|
||||
|
||||
ev := cli.conn.NextEvent()
|
||||
if ev.Kind != QUICErrorEvent {
|
||||
t.Errorf("client.NextEvent: no QUICErrorEvent, want one")
|
||||
}
|
||||
if ev.Err != err {
|
||||
t.Errorf("client.NextEvent: want same error returned by Start, got %v", ev.Err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we can report an error produced by the GetEncryptedClientHelloKeys function.
|
||||
func TestQUICECHKeyError(t *testing.T) {
|
||||
getECHKeysError := errors.New("error returned by GetEncryptedClientHelloKeys")
|
||||
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
||||
config.TLSConfig.MinVersion = VersionTLS13
|
||||
config.TLSConfig.NextProtos = []string{"h3"}
|
||||
config.TLSConfig.GetEncryptedClientHelloKeys = func(*ClientHelloInfo) ([]EncryptedClientHelloKey, error) {
|
||||
return nil, getECHKeysError
|
||||
}
|
||||
cli := newTestQUICClient(t, config)
|
||||
cli.conn.SetTransportParameters(nil)
|
||||
srv := newTestQUICServer(t, config)
|
||||
|
||||
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != errTransportParametersRequired {
|
||||
t.Fatalf("handshake with no client parameters: %v; want errTransportParametersRequired", err)
|
||||
}
|
||||
srv.conn.SetTransportParameters(nil)
|
||||
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err == nil {
|
||||
t.Fatalf("handshake with GetEncryptedClientHelloKeys errors: nil, want error")
|
||||
}
|
||||
if srv.gotError == nil {
|
||||
t.Fatalf("after GetEncryptedClientHelloKeys error, server did not see QUICErrorEvent")
|
||||
}
|
||||
if _, ok := errors.AsType[AlertError](srv.gotError); !ok {
|
||||
t.Errorf("connection handshake terminated with error %T, want AlertError", srv.gotError)
|
||||
}
|
||||
if !errors.Is(srv.gotError, getECHKeysError) {
|
||||
t.Errorf("connection handshake terminated with error %v, want error returned by GetEncryptedClientHelloKeys", srv.gotError)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that QUICConn.ConnectionState can be used during the handshake,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue