yuhan6665
2026-09-20 21:57:26 +00:00
committed by RPRX
parent 8cdf7bf9c7
commit 9124ee149d
25 changed files with 1491 additions and 1118 deletions
+57 -28
View File
@@ -8,6 +8,7 @@ import (
"context"
"errors"
"fmt"
"net"
)
// QUICEncryptionLevel represents a QUIC encryption level used to transmit
@@ -56,6 +57,9 @@ type QUICConfig struct {
// stored in the client session cache.
// The application should use [QUICConn.StoreSession] to store sessions.
EnableSessionEvents bool
// ClientHelloInfoConn is the net.Conn to use for the ClientHelloInfo.Conn field.
ClientHelloInfoConn net.Conn
}
// A QUICEventKind is a type of operation on a QUIC connection.
@@ -117,6 +121,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 +147,10 @@ type QUICEvent struct {
// Set for QUICResumeSession and QUICStoreSession.
SessionState *SessionState
// Set for QUICErrorEvent.
// The error will wrap AlertError.
Err error
}
type quicState struct {
@@ -153,10 +166,11 @@ type quicState struct {
started bool
signalc chan struct{} // handshake data is available to be read
blockedc chan struct{} // handshake is waiting for data, closed when done
cancelc <-chan struct{} // handshake has been canceled
ctx context.Context // handshake context
cancel context.CancelFunc
waitingForDrain bool
errorReturned bool
// readbuf is shared between HandleData and the handshake goroutine.
// HandshakeCryptoData passes ownership to the handshake goroutine by
@@ -166,23 +180,22 @@ type quicState struct {
transportParams []byte // to send to the peer
enableSessionEvents bool
clientHelloInfoConn net.Conn
}
// QUICClient returns a new TLS client side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func QUICClient(config *QUICConfig) *QUICConn {
return newQUICConn(Client(nil, config.TLSConfig), config)
}
// QUICServer returns a new TLS server side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func QUICServer(config *QUICConfig) *QUICConn {
//////////////////////////////////// [REALITY] SECTION: create Reality server
c, _ := Server(context.Background(), nil, config.TLSConfig)
return newQUICConn(c, config)
//////////////////////////////////// [REALITY] SECTION END
}
func newQUICConn(conn *Conn, config *QUICConfig) *QUICConn {
@@ -190,6 +203,7 @@ func newQUICConn(conn *Conn, config *QUICConfig) *QUICConn {
signalc: make(chan struct{}),
blockedc: make(chan struct{}),
enableSessionEvents: config.EnableSessionEvents,
clientHelloInfoConn: config.ClientHelloInfoConn,
}
conn.quic.events = conn.quic.eventArr[:0]
return &QUICConn{
@@ -206,9 +220,6 @@ func (q *QUICConn) Start(ctx context.Context) error {
return quicError(errors.New("tls: Start called more than once"))
}
q.conn.quic.started = true
if q.conn.config.MinVersion < VersionTLS13 {
return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3"))
}
go q.conn.HandshakeContext(ctx)
if _, ok := <-q.conn.quic.blockedc; !ok {
return q.conn.handshakeErr
@@ -222,7 +233,7 @@ func (q *QUICConn) NextEvent() QUICEvent {
qs := q.conn.quic
if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 {
// Write over some of the previous event's data,
// to catch callers erroniously retaining it.
// to catch callers erroneously retaining it.
qs.events[last].Data[0] = 0
}
if qs.nextEvent >= len(qs.events) && qs.waitingForDrain {
@@ -230,6 +241,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
@@ -243,10 +263,11 @@ func (q *QUICConn) NextEvent() QUICEvent {
// Close closes the connection and stops any in-progress handshake.
func (q *QUICConn) Close() error {
if q.conn.quic.cancel == nil {
if q.conn.quic.ctx == nil {
return nil // never started
}
q.conn.quic.cancel()
<-q.conn.quic.signalc
for range q.conn.quic.blockedc {
// Wait for the handshake goroutine to return.
}
@@ -270,9 +291,9 @@ func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
// The handshake goroutine has exited.
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
c.hand.Write(c.quic.readbuf)
c.handBuf().Write(c.quic.readbuf)
c.quic.readbuf = nil
for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil {
for q.conn.handLen() >= 4 && q.conn.handshakeErr == nil {
b := q.conn.hand.Bytes()
n := int(b[1])<<16 | int(b[2])<<8 | int(b[3])
if n > maxHandshake {
@@ -286,6 +307,7 @@ func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
q.conn.handshakeErr = err
}
}
q.conn.releaseHand()
if q.conn.handshakeErr != nil {
return quicError(q.conn.handshakeErr)
}
@@ -303,6 +325,9 @@ type QUICSessionTicketOptions struct {
// Currently, it can only be called once.
func (q *QUICConn) SendSessionTicket(opts QUICSessionTicketOptions) error {
c := q.conn
if c.config.SessionTicketsDisabled {
return nil
}
if !c.isHandshakeComplete.Load() {
return quicError(errors.New("tls: SendSessionTicket called before handshake completed"))
}
@@ -360,12 +385,11 @@ func quicError(err error) error {
if err == nil {
return nil
}
var ae AlertError
if errors.As(err, &ae) {
if _, ok := errors.AsType[AlertError](err); ok {
return err
}
var a alert
if !errors.As(err, &a) {
a, ok := errors.AsType[alert](err)
if !ok {
a = alertInternalError
}
// Return an error wrapping the original error and an AlertError.
@@ -374,7 +398,7 @@ func quicError(err error) error {
}
func (c *Conn) quicReadHandshakeBytes(n int) error {
for c.hand.Len() < n {
for c.handLen() < n {
if err := c.quicWaitForSignal(); err != nil {
return err
}
@@ -382,13 +406,22 @@ func (c *Conn) quicReadHandshakeBytes(n int) error {
return nil
}
func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) error {
// Ensure that there are no buffered handshake messages before changing the
// read keys, since that can cause messages to be parsed that were encrypted
// using old keys which are no longer appropriate.
// TODO(roland): we should merge this check with the similar one in setReadTrafficSecret.
if c.handLen() != 0 {
c.sendAlert(alertUnexpectedMessage)
return errors.New("tls: handshake buffer not empty before setting read traffic secret")
}
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICSetReadSecret,
Level: level,
Suite: suite,
Data: secret,
})
return nil
}
func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
@@ -482,20 +515,16 @@ func (c *Conn) quicWaitForSignal() error {
// Send on blockedc to notify the QUICConn that the handshake is blocked.
// Exported methods of QUICConn wait for the handshake to become blocked
// before returning to the user.
select {
case c.quic.blockedc <- struct{}{}:
case <-c.quic.cancelc:
return c.sendAlertLocked(alertCloseNotify)
}
c.quic.blockedc <- struct{}{}
// The QUICConn reads from signalc to notify us that the handshake may
// be able to proceed. (The QUICConn reads, because we close signalc to
// indicate that the handshake has completed.)
select {
case c.quic.signalc <- struct{}{}:
c.hand.Write(c.quic.readbuf)
c.quic.readbuf = nil
case <-c.quic.cancelc:
c.quic.signalc <- struct{}{}
if c.quic.ctx.Err() != nil {
// The connection has been canceled.
return c.sendAlertLocked(alertCloseNotify)
}
c.handBuf().Write(c.quic.readbuf)
c.quic.readbuf = nil
return nil
}