TUN-4922: Downgrade quic-go library to 0.20.0

This commit is contained in:
Sudarsan Reddy
2021-08-13 15:45:13 +01:00
parent 5f6e867685
commit 1082ac1c36
278 changed files with 3528 additions and 18344 deletions

View File

@@ -0,0 +1,5 @@
root = true
[*]
indent_style = tab
indent_size = 2

View File

@@ -8,12 +8,17 @@
[![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master)
[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go.
In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29), [draft-32](https://tools.ietf.org/html/draft-ietf-quic-transport-32) and [draft-34](https://tools.ietf.org/html/draft-ietf-quic-transport-34). Support for draft versions will be eventually be dropped, as these are phased out of the ecosystem.
quic-go is an implementation of the [QUIC](https://en.wikipedia.org/wiki/QUIC) protocol in Go. It implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29), [draft-32](https://tools.ietf.org/html/draft-ietf-quic-transport-32) and [draft-34](https://tools.ietf.org/html/draft-ietf-quic-transport-34).
## Version compatibility
Since quic-go is under active development, there's no guarantee that two builds of different commits are interoperable. The QUIC version used in the *master* branch is just a placeholder, and should not be considered stable.
When using quic-go as a library, please always use a [tagged release](https://github.com/lucas-clemente/quic-go/releases). Only these releases use the official draft version numbers.
## Guides
*We currently support Go 1.15.x, Go 1.16.x and Go 1.17 Beta 1, with [Go modules](https://github.com/golang/go/wiki/Modules) support enabled.*
*We currently support Go 1.15+, with [Go modules](https://github.com/golang/go/wiki/Modules) support enabled.*
Running tests:

View File

@@ -37,9 +37,8 @@ type client struct {
session quicSession
tracer logging.ConnectionTracer
tracingID uint64
logger utils.Logger
tracer logging.ConnectionTracer
logger utils.Logger
}
var (
@@ -203,16 +202,8 @@ func dialContext(
}
c.packetHandlers = packetHandlers
c.tracingID = nextSessionTracingID()
if c.config.Tracer != nil {
c.tracer = c.config.Tracer.TracerForConnection(
context.WithValue(ctx, SessionTracingKey, c.tracingID),
protocol.PerspectiveClient,
c.destConnID,
)
}
if c.tracer != nil {
c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID)
c.tracer = c.config.Tracer.TracerForConnection(protocol.PerspectiveClient, c.destConnID)
}
if err := c.dial(ctx); err != nil {
return nil, err
@@ -279,6 +270,9 @@ func newClient(
func (c *client) dial(ctx context.Context) error {
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
if c.tracer != nil {
c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.version, c.srcConnID, c.destConnID)
}
c.session = newClientSession(
c.conn,
@@ -291,7 +285,6 @@ func (c *client) dial(ctx context.Context) error {
c.use0RTT,
c.hasNegotiatedVersion,
c.tracer,
c.tracingID,
c.logger,
c.version,
)
@@ -300,7 +293,7 @@ func (c *client) dial(ctx context.Context) error {
errorChan := make(chan error, 1)
go func() {
err := c.session.run() // returns as soon as the session is closed
if !errors.Is(err, &errCloseForRecreating{}) && c.createdPacketConn {
if !errors.Is(err, errCloseForRecreating{}) && c.createdPacketConn {
c.packetHandlers.Destroy()
}
errorChan <- err

View File

@@ -73,10 +73,7 @@ func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error {
func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.ConnectionID) error {
if seq > m.highestSeq {
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: fmt.Sprintf("retired connection ID %d (highest issued: %d)", seq, m.highestSeq),
}
return qerr.NewError(qerr.ProtocolViolation, fmt.Sprintf("tried to retire connection ID %d. Highest issued: %d", seq, m.highestSeq))
}
connID, ok := m.activeSrcConnIDs[seq]
// We might already have deleted this connection ID, if this is a duplicate frame.
@@ -84,10 +81,7 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect
return nil
}
if connID.Equal(sentWithDestConnID) && !protocol.UseRetireBugBackwardsCompatibilityMode(RetireBugBackwardsCompatibilityMode, m.version) {
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID),
}
return qerr.NewError(qerr.ProtocolViolation, fmt.Sprintf("tried to retire connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID))
}
m.retireConnectionID(connID)
delete(m.activeSrcConnIDs, seq)

View File

@@ -53,7 +53,7 @@ func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error {
return err
}
if h.queue.Len() >= protocol.MaxActiveConnectionIDs {
return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}
return qerr.ConnectionIDLimitError
}
return nil
}

View File

@@ -11,21 +11,7 @@ import (
"golang.org/x/sys/windows"
)
const IP_DONTFRAGMENT = 14
func newConn(c OOBCapablePacketConn) (connection, error) {
rawConn, err := c.SyscallConn()
if err != nil {
return nil, fmt.Errorf("couldn't get syscall.RawConn: %w", err)
}
if err := rawConn.Control(func(fd uintptr) {
// This should succeed if the connection is a IPv4 or a dual-stack connection.
// It will fail for IPv6 connections.
// TODO: properly handle error.
_ = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1)
}); err != nil {
return nil, err
}
func newConn(c net.PacketConn) (connection, error) {
return &basicConn{PacketConn: c}, nil
}

View File

@@ -39,18 +39,12 @@ func newCryptoStream() cryptoStream {
func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error {
highestOffset := f.Offset + protocol.ByteCount(len(f.Data))
if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset {
return &qerr.TransportError{
ErrorCode: qerr.CryptoBufferExceeded,
ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset),
}
return qerr.NewError(qerr.CryptoBufferExceeded, fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset))
}
if s.finished {
if highestOffset > s.highestOffset {
// reject crypto data received after this stream was already finished
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: "received crypto data after change of encryption level",
}
return qerr.NewError(qerr.ProtocolViolation, "received crypto data after change of encryption level")
}
// ignore data with a smaller offset than the highest received
// could e.g. be a retransmission
@@ -86,10 +80,7 @@ func (s *cryptoStreamImpl) GetCryptoData() []byte {
func (s *cryptoStreamImpl) Finish() error {
if s.queue.HasMoreData() {
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: "encryption level changed, but crypto stream has more data to read",
}
return qerr.NewError(qerr.ProtocolViolation, "encryption level changed, but crypto stream has more data to read")
}
s.finished = true
return nil

View File

@@ -1,58 +0,0 @@
package quic
import (
"fmt"
"github.com/lucas-clemente/quic-go/internal/qerr"
)
type (
TransportError = qerr.TransportError
ApplicationError = qerr.ApplicationError
VersionNegotiationError = qerr.VersionNegotiationError
StatelessResetError = qerr.StatelessResetError
IdleTimeoutError = qerr.IdleTimeoutError
HandshakeTimeoutError = qerr.HandshakeTimeoutError
)
type (
TransportErrorCode = qerr.TransportErrorCode
ApplicationErrorCode = qerr.ApplicationErrorCode
StreamErrorCode = qerr.StreamErrorCode
)
const (
NoError = qerr.NoError
InternalError = qerr.InternalError
ConnectionRefused = qerr.ConnectionRefused
FlowControlError = qerr.FlowControlError
StreamLimitError = qerr.StreamLimitError
StreamStateError = qerr.StreamStateError
FinalSizeError = qerr.FinalSizeError
FrameEncodingError = qerr.FrameEncodingError
TransportParameterError = qerr.TransportParameterError
ConnectionIDLimitError = qerr.ConnectionIDLimitError
ProtocolViolation = qerr.ProtocolViolation
InvalidToken = qerr.InvalidToken
ApplicationErrorErrorCode = qerr.ApplicationErrorErrorCode
CryptoBufferExceeded = qerr.CryptoBufferExceeded
KeyUpdateError = qerr.KeyUpdateError
AEADLimitReached = qerr.AEADLimitReached
NoViablePathError = qerr.NoViablePathError
)
// A StreamError is used for Stream.CancelRead and Stream.CancelWrite.
// It is also returned from Stream.Read and Stream.Write if the peer canceled reading or writing.
type StreamError struct {
StreamID StreamID
ErrorCode StreamErrorCode
}
func (e *StreamError) Is(target error) bool {
_, ok := target.(*StreamError)
return ok
}
func (e *StreamError) Error() string {
return fmt.Sprintf("stream %d canceled with error code %d", e.StreamID, e.ErrorCode)
}

View File

@@ -1,20 +1,19 @@
module github.com/lucas-clemente/quic-go
go 1.15
go 1.14
require (
github.com/cheekybits/genny v1.0.0
github.com/francoispqt/gojay v1.2.13
github.com/golang/mock v1.6.0
github.com/golang/mock v1.5.0
github.com/marten-seemann/qpack v0.2.1
github.com/marten-seemann/qtls-go1-15 v0.1.4
github.com/marten-seemann/qtls-go1-16 v0.1.3
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2
github.com/onsi/ginkgo v1.14.0
github.com/onsi/gomega v1.10.1
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
golang.org/x/net v0.0.0-20200707034311-ab3426394381
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)

View File

@@ -34,8 +34,8 @@ github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200j
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -79,8 +79,6 @@ github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9M
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU=
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2 h1:SficYjyOthSrliKI+EaFuXS6HqSsX3dkY9AqxAAjBjw=
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -135,7 +133,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
@@ -150,7 +147,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -163,9 +160,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -176,8 +172,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -190,16 +186,12 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg=
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -209,12 +201,10 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=

View File

@@ -33,10 +33,6 @@ const (
VersionDraft29 = protocol.VersionDraft29
// VersionDraft32 is IETF QUIC draft-32
VersionDraft32 = protocol.VersionDraft32
// VersionDraft34 is IETF QUIC draft-34
VersionDraft34 = protocol.VersionDraft34
// Version1 is RFC 9000
Version1 = protocol.Version1
)
// A Token can be used to verify the ownership of the client address.
@@ -66,6 +62,10 @@ type TokenStore interface {
Put(key string, token *ClientToken)
}
// An ErrorCode is an application-defined error code.
// Valid values range between 0 and MAX_UINT62.
type ErrorCode = protocol.ApplicationErrorCode
// Err0RTTRejected is the returned from:
// * Open{Uni}Stream{Sync}
// * Accept{Uni}Stream
@@ -73,16 +73,7 @@ type TokenStore interface {
// when the server rejects a 0-RTT connection attempt.
var Err0RTTRejected = errors.New("0-RTT rejected")
// SessionTracingKey can be used to associate a ConnectionTracer with a Session.
// It is set on the Session.Context() context,
// as well as on the context passed to logging.Tracer.NewConnectionTracer.
var SessionTracingKey = sessionTracingCtxKey{}
type sessionTracingCtxKey struct{}
// Stream is the interface implemented by QUIC streams
// In addition to the errors listed on the Session,
// calls to stream functions can return a StreamError if the stream is canceled.
type Stream interface {
ReceiveStream
SendStream
@@ -108,7 +99,7 @@ type ReceiveStream interface {
// It will ask the peer to stop transmitting stream data.
// Read will unblock immediately, and future Read calls will fail.
// When called multiple times or after reading the io.EOF it is a no-op.
CancelRead(StreamErrorCode)
CancelRead(ErrorCode)
// SetReadDeadline sets the deadline for future Read calls and
// any currently-blocked Read call.
// A zero value for t means Read will not time out.
@@ -137,7 +128,7 @@ type SendStream interface {
// Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably.
// Write will unblock immediately, and future calls to Write will fail.
// When called multiple times or after closing the stream it is a no-op.
CancelWrite(StreamErrorCode)
CancelWrite(ErrorCode)
// The context is canceled as soon as the write-side of the stream is closed.
// This happens when Close() or CancelWrite() is called, or when the peer
// cancels the read-side of their stream.
@@ -151,14 +142,14 @@ type SendStream interface {
SetWriteDeadline(t time.Time) error
}
// StreamError is returned by Read and Write when the peer cancels the stream.
type StreamError interface {
error
Canceled() bool
ErrorCode() ErrorCode
}
// A Session is a QUIC connection between two peers.
// Calls to the session (and to streams) can return the following types of errors:
// * ApplicationError: for errors triggered by the application running on top of QUIC
// * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer)
// * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error)
// * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error)
// * StatelessResetError: when we receive a stateless reset (this is a net.Error temporary error)
// * VersionNegotiationError: returned by the client, when there's no version overlap between the peers
type Session interface {
// AcceptStream returns the next stream opened by the peer, blocking until one is available.
// If the session was closed due to a timeout, the error satisfies
@@ -194,9 +185,9 @@ type Session interface {
LocalAddr() net.Addr
// RemoteAddr returns the address of the peer.
RemoteAddr() net.Addr
// CloseWithError closes the connection with an error.
// Close the connection with an error.
// The error string will be sent to the peer.
CloseWithError(ApplicationErrorCode, string) error
CloseWithError(ErrorCode, string) error
// The context is cancelled when the session is closed.
// Warning: This API should not be considered stable and might change soon.
Context() context.Context

View File

@@ -9,13 +9,12 @@ import (
// NewAckHandler creates a new SentPacketHandler and a new ReceivedPacketHandler
func NewAckHandler(
initialPacketNumber protocol.PacketNumber,
initialMaxDatagramSize protocol.ByteCount,
rttStats *utils.RTTStats,
pers protocol.Perspective,
tracer logging.ConnectionTracer,
logger utils.Logger,
version protocol.VersionNumber,
) (SentPacketHandler, ReceivedPacketHandler) {
sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, pers, tracer, logger)
sph := newSentPacketHandler(initialPacketNumber, rttStats, pers, tracer, logger)
return sph, newReceivedPacketHandler(sph, rttStats, logger, version)
}

View File

@@ -27,7 +27,7 @@ type Packet struct {
type SentPacketHandler interface {
// SentPacket may modify the packet
SentPacket(packet *Packet)
ReceivedAck(ackFrame *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) (bool /* 1-RTT packet acked */, error)
ReceivedAck(ackFrame *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) error
ReceivedBytes(protocol.ByteCount)
DropPackets(protocol.EncryptionLevel)
ResetForRetry() error

View File

@@ -21,8 +21,6 @@ const (
packetThreshold = 3
// Before validating the client's address, the server won't send more than 3x bytes than it received.
amplificationFactor = 3
// We use Retry packets to derive an RTT estimate. Make sure we don't set the RTT to a super low value yet.
minRTTAfterRetry = 5 * time.Millisecond
)
type packetNumberSpace struct {
@@ -103,7 +101,6 @@ var (
func newSentPacketHandler(
initialPN protocol.PacketNumber,
initialMaxDatagramSize protocol.ByteCount,
rttStats *utils.RTTStats,
pers protocol.Perspective,
tracer logging.ConnectionTracer,
@@ -112,7 +109,6 @@ func newSentPacketHandler(
congestion := congestion.NewCubicSender(
congestion.DefaultClock{},
rttStats,
initialMaxDatagramSize,
true, // use Reno
tracer,
)
@@ -198,17 +194,12 @@ func (h *sentPacketHandler) dropPackets(encLevel protocol.EncryptionLevel) {
}
func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount) {
wasAmplificationLimit := h.isAmplificationLimited()
h.bytesReceived += n
if wasAmplificationLimit && !h.isAmplificationLimited() {
h.setLossDetectionTimer()
}
}
func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel) {
if h.perspective == protocol.PerspectiveServer && l == protocol.EncryptionHandshake && !h.peerAddressValidated {
func (h *sentPacketHandler) ReceivedPacket(encLevel protocol.EncryptionLevel) {
if h.perspective == protocol.PerspectiveServer && encLevel == protocol.EncryptionHandshake {
h.peerAddressValidated = true
h.setLossDetectionTimer()
}
}
@@ -277,15 +268,12 @@ func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* is ack-elicit
return isAckEliciting
}
func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* contained 1-RTT packet */, error) {
func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) error {
pnSpace := h.getPacketNumberSpace(encLevel)
largestAcked := ack.LargestAcked()
if largestAcked > pnSpace.largestSent {
return false, &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: "received ACK for an unsent packet",
}
return qerr.NewError(qerr.ProtocolViolation, "Received ACK for an unsent packet")
}
pnSpace.largestAcked = utils.MaxPacketNumber(pnSpace.largestAcked, largestAcked)
@@ -302,7 +290,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
priorInFlight := h.bytesInFlight
ackedPackets, err := h.detectAndRemoveAckedPackets(ack, encLevel)
if err != nil || len(ackedPackets) == 0 {
return false, err
return err
}
// update the RTT, if the largest acked is newly acknowledged
if len(ackedPackets) > 0 {
@@ -320,16 +308,15 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
}
}
if err := h.detectLostPackets(rcvTime, encLevel); err != nil {
return false, err
return err
}
var acked1RTTPacket bool
for _, p := range ackedPackets {
if p.skippedPacket {
return fmt.Errorf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel)
}
if p.includedInBytesInFlight && !p.declaredLost {
h.congestion.OnPacketAcked(p.PacketNumber, p.Length, priorInFlight, rcvTime)
}
if p.EncryptionLevel == protocol.Encryption1RTT {
acked1RTTPacket = true
}
h.removeFromBytesInFlight(p)
}
@@ -348,7 +335,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
pnSpace.history.DeleteOldPackets(rcvTime)
h.setLossDetectionTimer()
return acked1RTTPacket, nil
return nil
}
func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber {
@@ -380,20 +367,15 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
ackRange = ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex]
}
if p.PacketNumber < ackRange.Smallest { // packet not contained in ACK range
return true, nil
}
if p.PacketNumber > ackRange.Largest {
return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest)
if p.PacketNumber >= ackRange.Smallest { // packet i contained in ACK range
if p.PacketNumber > ackRange.Largest {
return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest)
}
h.ackedPackets = append(h.ackedPackets, p)
}
} else {
h.ackedPackets = append(h.ackedPackets, p)
}
if p.skippedPacket {
return false, &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel),
}
}
h.ackedPackets = append(h.ackedPackets, p)
return true, nil
})
if h.logger.Debug() && len(h.ackedPackets) > 0 {
@@ -417,9 +399,6 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
if err := pnSpace.history.Remove(p.PacketNumber); err != nil {
return nil, err
}
if h.tracer != nil {
h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber)
}
}
return h.ackedPackets, err
@@ -445,20 +424,20 @@ func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.Encryptio
}
// same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime
func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) {
// We only send application data probe packets once the handshake is confirmed,
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
if !h.handshakeConfirmed && !h.hasOutstandingCryptoPackets() {
if h.peerCompletedAddressValidation {
return
}
func (h *sentPacketHandler) getPTOTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
if !h.hasOutstandingPackets() {
t := time.Now().Add(h.rttStats.PTO(false) << h.ptoCount)
if h.initialPackets != nil {
return t, protocol.EncryptionInitial, true
return t, protocol.EncryptionInitial
}
return t, protocol.EncryptionHandshake, true
return t, protocol.EncryptionHandshake
}
var (
encLevel protocol.EncryptionLevel
pto time.Time
)
if h.initialPackets != nil {
encLevel = protocol.EncryptionInitial
if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() {
@@ -479,7 +458,7 @@ func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protoc
encLevel = protocol.Encryption1RTT
}
}
return pto, encLevel, true
return pto, encLevel
}
func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
@@ -494,13 +473,15 @@ func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
}
func (h *sentPacketHandler) hasOutstandingPackets() bool {
return h.appDataPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets()
// We only send application data probe packets once the handshake completes,
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
return (h.handshakeConfirmed && h.appDataPackets.history.HasOutstandingPackets()) ||
h.hasOutstandingCryptoPackets()
}
func (h *sentPacketHandler) setLossDetectionTimer() {
oldAlarm := h.alarm // only needed in case tracing is enabled
lossTime, encLevel := h.getLossTimeAndSpace()
if !lossTime.IsZero() {
if lossTime, encLevel := h.getLossTimeAndSpace(); !lossTime.IsZero() {
// Early retransmit timer or time loss detection.
h.alarm = lossTime
if h.tracer != nil && h.alarm != oldAlarm {
@@ -509,35 +490,18 @@ func (h *sentPacketHandler) setLossDetectionTimer() {
return
}
// Cancel the alarm if amplification limited.
if h.isAmplificationLimited() {
h.alarm = time.Time{}
if !oldAlarm.IsZero() {
h.logger.Debugf("Canceling loss detection timer. Amplification limited.")
if h.tracer != nil {
h.tracer.LossTimerCanceled()
}
}
return
}
// Cancel the alarm if no packets are outstanding
if !h.hasOutstandingPackets() && h.peerCompletedAddressValidation {
h.alarm = time.Time{}
if !oldAlarm.IsZero() {
h.logger.Debugf("Canceling loss detection timer. No packets in flight.")
if h.tracer != nil {
h.tracer.LossTimerCanceled()
}
h.logger.Debugf("Canceling loss detection timer. No packets in flight.")
if h.tracer != nil && !oldAlarm.IsZero() {
h.tracer.LossTimerCanceled()
}
return
}
// PTO alarm
ptoTime, encLevel, ok := h.getPTOTimeAndSpace()
if !ok {
return
}
ptoTime, encLevel := h.getPTOTimeAndSpace()
h.alarm = ptoTime
if h.tracer != nil && h.alarm != oldAlarm {
h.tracer.SetLossTimer(logging.TimerTypePTO, encLevel, h.alarm)
@@ -605,7 +569,20 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E
}
func (h *sentPacketHandler) OnLossDetectionTimeout() error {
defer h.setLossDetectionTimer()
// When all outstanding are acknowledged, the alarm is canceled in
// setLossDetectionTimer. This doesn't reset the timer in the session though.
// When OnAlarm is called, we therefore need to make sure that there are
// actually packets outstanding.
if h.hasOutstandingPackets() || !h.peerCompletedAddressValidation {
if err := h.onVerifiedLossDetectionTimeout(); err != nil {
return err
}
}
h.setLossDetectionTimer()
return nil
}
func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
earliestLossTime, encLevel := h.getLossTimeAndSpace()
if !earliestLossTime.IsZero() {
if h.logger.Debug() {
@@ -619,12 +596,34 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
}
// PTO
// When all outstanding are acknowledged, the alarm is canceled in
// setLossDetectionTimer. This doesn't reset the timer in the session though.
// When OnAlarm is called, we therefore need to make sure that there are
// actually packets outstanding.
if h.bytesInFlight == 0 && !h.peerCompletedAddressValidation {
h.ptoCount++
h.ptoCount++
if h.bytesInFlight > 0 {
_, encLevel = h.getPTOTimeAndSpace()
if h.logger.Debug() {
h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount)
}
if h.tracer != nil {
h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel)
h.tracer.UpdatedPTOCount(h.ptoCount)
}
h.numProbesToSend += 2
//nolint:exhaustive // We never arm a PTO timer for 0-RTT packets.
switch encLevel {
case protocol.EncryptionInitial:
h.ptoMode = SendPTOInitial
case protocol.EncryptionHandshake:
h.ptoMode = SendPTOHandshake
case protocol.Encryption1RTT:
// skip a packet number in order to elicit an immediate ACK
_ = h.PopPacketNumber(protocol.Encryption1RTT)
h.ptoMode = SendPTOAppData
default:
return fmt.Errorf("PTO timer in unexpected encryption level: %s", encLevel)
}
} else {
if h.perspective == protocol.PerspectiveServer {
return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0")
}
h.numProbesToSend++
if h.initialPackets != nil {
h.ptoMode = SendPTOInitial
@@ -633,37 +632,6 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
} else {
return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0 and Initial and Handshake already dropped")
}
return nil
}
_, encLevel, ok := h.getPTOTimeAndSpace()
if !ok {
return nil
}
if ps := h.getPacketNumberSpace(encLevel); !ps.history.HasOutstandingPackets() && !h.peerCompletedAddressValidation {
return nil
}
h.ptoCount++
if h.logger.Debug() {
h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount)
}
if h.tracer != nil {
h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel)
h.tracer.UpdatedPTOCount(h.ptoCount)
}
h.numProbesToSend += 2
//nolint:exhaustive // We never arm a PTO timer for 0-RTT packets.
switch encLevel {
case protocol.EncryptionInitial:
h.ptoMode = SendPTOInitial
case protocol.EncryptionHandshake:
h.ptoMode = SendPTOHandshake
case protocol.Encryption1RTT:
// skip a packet number in order to elicit an immediate ACK
_ = h.PopPacketNumber(protocol.Encryption1RTT)
h.ptoMode = SendPTOAppData
default:
return fmt.Errorf("PTO timer in unexpected encryption level: %s", encLevel)
}
return nil
}
@@ -800,9 +768,8 @@ func (h *sentPacketHandler) ResetForRetry() error {
// Only use the Retry to estimate the RTT if we didn't send any retransmission for the Initial.
// Otherwise, we don't know which Initial the Retry was sent in response to.
if h.ptoCount == 0 {
// Don't set the RTT to a value lower than 5ms here.
now := time.Now()
h.rttStats.UpdateRTT(utils.MaxDuration(minRTTAfterRetry, now.Sub(firstPacketSendTime)), 0, now)
h.rttStats.UpdateRTT(now.Sub(firstPacketSendTime), 0, now)
if h.logger.Debug() {
h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
}

View File

@@ -64,9 +64,8 @@ func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) err
// FirstOutStanding returns the first outstanding packet.
func (h *sentPacketHistory) FirstOutstanding() *Packet {
for el := h.packetList.Front(); el != nil; el = el.Next() {
p := &el.Value
if !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket {
return p
if !el.Value.declaredLost && !el.Value.skippedPacket {
return &el.Value
}
}
return nil

View File

@@ -1,7 +1,6 @@
package congestion
import (
"fmt"
"time"
"github.com/lucas-clemente/quic-go/internal/protocol"
@@ -15,8 +14,9 @@ const (
initialMaxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4)
maxBurstPackets = 3
renoBeta = 0.7 // Reno backoff factor.
initialMaxCongestionWindow = protocol.MaxCongestionWindowPackets * initialMaxDatagramSize
minCongestionWindowPackets = 2
initialCongestionWindow = 32
initialCongestionWindow = 32 * initialMaxDatagramSize
)
type cubicSender struct {
@@ -65,33 +65,11 @@ var (
)
// NewCubicSender makes a new cubic sender
func NewCubicSender(
clock Clock,
rttStats *utils.RTTStats,
initialMaxDatagramSize protocol.ByteCount,
reno bool,
tracer logging.ConnectionTracer,
) *cubicSender {
return newCubicSender(
clock,
rttStats,
reno,
initialMaxDatagramSize,
initialCongestionWindow*initialMaxDatagramSize,
protocol.MaxCongestionWindowPackets*initialMaxDatagramSize,
tracer,
)
func NewCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, tracer logging.ConnectionTracer) *cubicSender {
return newCubicSender(clock, rttStats, reno, initialCongestionWindow, initialMaxCongestionWindow, tracer)
}
func newCubicSender(
clock Clock,
rttStats *utils.RTTStats,
reno bool,
initialMaxDatagramSize,
initialCongestionWindow,
initialMaxCongestionWindow protocol.ByteCount,
tracer logging.ConnectionTracer,
) *cubicSender {
func newCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount, tracer logging.ConnectionTracer) *cubicSender {
c := &cubicSender{
rttStats: rttStats,
largestSentPacketNumber: protocol.InvalidPacketNumber,
@@ -305,7 +283,7 @@ func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) {
func (c *cubicSender) SetMaxDatagramSize(s protocol.ByteCount) {
if s < c.maxDatagramSize {
panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", c.maxDatagramSize, s))
panic("congestion BUG: decreased max datagram size")
}
cwndIsMinCwnd := c.congestionWindow == c.minCongestionWindow()
c.maxDatagramSize = s

View File

@@ -50,10 +50,7 @@ func (c *connectionFlowController) IncrementHighestReceived(increment protocol.B
c.highestReceived += increment
if c.checkFlowControlViolation() {
return &qerr.TransportError{
ErrorCode: qerr.FlowControlError,
ErrorMessage: fmt.Sprintf("received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow),
}
return qerr.NewError(qerr.FlowControlError, fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow))
}
return nil
}

View File

@@ -54,17 +54,11 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
if c.receivedFinalOffset {
// If we receive another final offset, check that it's the same.
if final && offset != c.highestReceived {
return &qerr.TransportError{
ErrorCode: qerr.FinalSizeError,
ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset),
}
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset))
}
// Check that the offset is below the final offset.
if offset > c.highestReceived {
return &qerr.TransportError{
ErrorCode: qerr.FinalSizeError,
ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", offset, c.streamID, c.highestReceived),
}
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received offset %d for stream %d. Final offset was already received at %d", offset, c.streamID, c.highestReceived))
}
}
@@ -78,10 +72,7 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
// This can happen due to reordering.
if offset <= c.highestReceived {
if final {
return &qerr.TransportError{
ErrorCode: qerr.FinalSizeError,
ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived),
}
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived))
}
return nil
}
@@ -89,10 +80,7 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
increment := offset - c.highestReceived
c.highestReceived = offset
if c.checkFlowControlViolation() {
return &qerr.TransportError{
ErrorCode: qerr.FlowControlError,
ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow),
}
return qerr.NewError(qerr.FlowControlError, fmt.Sprintf("Received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow))
}
return c.connection.IncrementHighestReceived(increment)
}

View File

@@ -403,10 +403,7 @@ func (h *cryptoSetup) checkEncryptionLevel(msgType messageType, encLevel protoco
func (h *cryptoSetup) handleTransportParameters(data []byte) {
var tp wire.TransportParameters
if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil {
h.runner.OnError(&qerr.TransportError{
ErrorCode: qerr.TransportParameterError,
ErrorMessage: err.Error(),
})
h.runner.OnError(qerr.NewError(qerr.TransportParameterError, err.Error()))
}
h.peerParams = &tp
h.runner.OnReceivedParams(h.peerParams)
@@ -558,7 +555,7 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph
newHeaderProtector(suite, trafficSecret, true),
)
h.mutex.Unlock()
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
if h.tracer != nil {
h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective.Opposite())
}
@@ -571,12 +568,12 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph
h.dropInitialKeys,
h.perspective,
)
h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID))
h.logger.Debugf("Installed Handshake Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
case qtls.EncryptionApplication:
h.readEncLevel = protocol.Encryption1RTT
h.aead.SetReadKey(suite, trafficSecret)
h.has1RTTOpener = true
h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
h.logger.Debugf("Installed 1-RTT Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
default:
panic("unexpected read encryption level")
}
@@ -598,7 +595,7 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip
newHeaderProtector(suite, trafficSecret, true),
)
h.mutex.Unlock()
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
if h.tracer != nil {
h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective)
}
@@ -611,12 +608,12 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip
h.dropInitialKeys,
h.perspective,
)
h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID))
h.logger.Debugf("Installed Handshake Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
case qtls.EncryptionApplication:
h.writeEncLevel = protocol.Encryption1RTT
h.aead.SetWriteKey(suite, trafficSecret)
h.has1RTTSealer = true
h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
h.logger.Debugf("Installed 1-RTT Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
if h.zeroRTTSealer != nil {
h.zeroRTTSealer = nil
h.logger.Debugf("Dropping 0-RTT keys.")

View File

@@ -4,8 +4,6 @@ import (
"crypto"
"crypto/tls"
"golang.org/x/crypto/hkdf"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qtls"
)
@@ -16,7 +14,7 @@ var (
)
func getSalt(v protocol.VersionNumber) []byte {
if v == protocol.VersionDraft34 || v == protocol.Version1 {
if v == protocol.VersionDraft34 {
return quicSaltDraft34
}
return quicSaltOld
@@ -51,7 +49,7 @@ func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v p
}
func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) {
initialSecret := hkdf.Extract(crypto.SHA256.New, connID, getSalt(v))
initialSecret := qtls.HkdfExtract(crypto.SHA256, connID, getSalt(v))
clientSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
serverSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size())
return

View File

@@ -48,7 +48,7 @@ func GetRetryIntegrityTag(retry []byte, origDestConnID protocol.ConnectionID, ve
var tag [16]byte
var sealed []byte
if version != protocol.VersionDraft34 && version != protocol.Version1 {
if version != protocol.VersionDraft34 {
sealed = oldRetryAEAD.Seal(tag[:0], oldRetryNonce[:], nil, retryBuf.Bytes())
} else {
sealed = retryAEAD.Seal(tag[:0], retryNonce[:], nil, retryBuf.Bytes())

View File

@@ -24,7 +24,7 @@ var _ tlsExtensionHandler = &extensionHandler{}
// newExtensionHandler creates a new extension handler
func newExtensionHandler(params []byte, pers protocol.Perspective, v protocol.VersionNumber) tlsExtensionHandler {
et := uint16(quicTLSExtensionType)
if v != protocol.VersionDraft34 && v != protocol.Version1 {
if v != protocol.VersionDraft34 {
et = quicTLSExtensionTypeOldDrafts
}
return &extensionHandler{

View File

@@ -163,7 +163,7 @@ func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.Pac
if err == ErrDecryptionFailed {
a.invalidPacketCount++
if a.invalidPacketCount >= a.invalidPacketLimit {
return nil, &qerr.TransportError{ErrorCode: qerr.AEADLimitReached}
return nil, qerr.AEADLimitReached
}
}
if err == nil {
@@ -201,10 +201,7 @@ func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.Pac
}
// Opening succeeded. Check if the peer was allowed to update.
if a.keyPhase > 0 && a.firstSentWithCurrentKey == protocol.InvalidPacketNumber {
return nil, &qerr.TransportError{
ErrorCode: qerr.KeyUpdateError,
ErrorMessage: "keys updated too quickly",
}
return nil, qerr.NewError(qerr.KeyUpdateError, "keys updated too quickly")
}
a.rollKeys()
a.logger.Debugf("Peer updated keys to %d", a.keyPhase)
@@ -253,10 +250,7 @@ func (a *updatableAEAD) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byt
func (a *updatableAEAD) SetLargestAcked(pn protocol.PacketNumber) error {
if a.firstSentWithCurrentKey != protocol.InvalidPacketNumber &&
pn >= a.firstSentWithCurrentKey && a.numRcvdWithCurrentKey == 0 {
return &qerr.TransportError{
ErrorCode: qerr.KeyUpdateError,
ErrorMessage: fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase),
}
return qerr.NewError(qerr.KeyUpdateError, fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase))
}
a.largestAcked = pn
return nil

View File

@@ -52,6 +52,9 @@ const MaxByteCount = ByteCount(1<<62 - 1)
// InvalidByteCount is an invalid byte count
const InvalidByteCount ByteCount = -1
// An ApplicationErrorCode is an application-defined error code.
type ApplicationErrorCode uint64
// A StatelessResetToken is a stateless reset token.
type StatelessResetToken [16]byte

View File

@@ -18,18 +18,17 @@ const (
// The version numbers, making grepping easier
const (
VersionTLS VersionNumber = 0x1
VersionWhatever VersionNumber = math.MaxUint32 - 1 // for when the version doesn't matter
VersionTLS VersionNumber = 0xff00001d // draft-29
VersionWhatever VersionNumber = 1 // for when the version doesn't matter
VersionUnknown VersionNumber = math.MaxUint32
VersionDraft29 VersionNumber = 0xff00001d
VersionDraft32 VersionNumber = 0xff000020
VersionDraft34 VersionNumber = 0xff000022
Version1 VersionNumber = 0x1
VersionDraft34 VersionNumber = 0xff000022 // If everything goes according to plan at the IETF, this will one day be QUIC v1.
)
// SupportedVersions lists the versions that the server supports
// must be in sorted descending order
var SupportedVersions = []VersionNumber{Version1, VersionDraft34, VersionDraft32, VersionDraft29}
var SupportedVersions = []VersionNumber{VersionDraft29, VersionDraft34, VersionDraft32}
// IsValidVersion says if the version is known to quic-go
func IsValidVersion(v VersionNumber) bool {
@@ -39,7 +38,7 @@ func IsValidVersion(v VersionNumber) bool {
func (vn VersionNumber) String() string {
// For releases, VersionTLS will be set to a draft version.
// A switch statement can't contain duplicate cases.
if vn == VersionTLS && VersionTLS != VersionDraft29 && VersionTLS != VersionDraft32 && VersionTLS != Version1 {
if vn == VersionTLS && VersionTLS != VersionDraft29 && VersionTLS != VersionDraft32 {
return "TLS dev version (WIP)"
}
//nolint:exhaustive
@@ -54,8 +53,6 @@ func (vn VersionNumber) String() string {
return "draft-32"
case VersionDraft34:
return "draft-34"
case Version1:
return "v1"
default:
if vn.isGQUIC() {
return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion())

View File

@@ -6,44 +6,51 @@ import (
"github.com/lucas-clemente/quic-go/internal/qtls"
)
// TransportErrorCode is a QUIC transport error.
type TransportErrorCode uint64
// ErrorCode can be used as a normal error without reason.
type ErrorCode uint64
// The error codes defined by QUIC
const (
NoError TransportErrorCode = 0x0
InternalError TransportErrorCode = 0x1
ConnectionRefused TransportErrorCode = 0x2
FlowControlError TransportErrorCode = 0x3
StreamLimitError TransportErrorCode = 0x4
StreamStateError TransportErrorCode = 0x5
FinalSizeError TransportErrorCode = 0x6
FrameEncodingError TransportErrorCode = 0x7
TransportParameterError TransportErrorCode = 0x8
ConnectionIDLimitError TransportErrorCode = 0x9
ProtocolViolation TransportErrorCode = 0xa
InvalidToken TransportErrorCode = 0xb
ApplicationErrorErrorCode TransportErrorCode = 0xc
CryptoBufferExceeded TransportErrorCode = 0xd
KeyUpdateError TransportErrorCode = 0xe
AEADLimitReached TransportErrorCode = 0xf
NoViablePathError TransportErrorCode = 0x10
NoError ErrorCode = 0x0
InternalError ErrorCode = 0x1
ConnectionRefused ErrorCode = 0x2
FlowControlError ErrorCode = 0x3
StreamLimitError ErrorCode = 0x4
StreamStateError ErrorCode = 0x5
FinalSizeError ErrorCode = 0x6
FrameEncodingError ErrorCode = 0x7
TransportParameterError ErrorCode = 0x8
ConnectionIDLimitError ErrorCode = 0x9
ProtocolViolation ErrorCode = 0xa
InvalidToken ErrorCode = 0xb
ApplicationError ErrorCode = 0xc
CryptoBufferExceeded ErrorCode = 0xd
KeyUpdateError ErrorCode = 0xe
AEADLimitReached ErrorCode = 0xf
NoViablePathError ErrorCode = 0x10
)
func (e TransportErrorCode) IsCryptoError() bool {
func (e ErrorCode) isCryptoError() bool {
return e >= 0x100 && e < 0x200
}
func (e ErrorCode) Error() string {
if e.isCryptoError() {
return fmt.Sprintf("%s: %s", e.String(), e.Message())
}
return e.String()
}
// Message is a description of the error.
// It only returns a non-empty string for crypto errors.
func (e TransportErrorCode) Message() string {
if !e.IsCryptoError() {
func (e ErrorCode) Message() string {
if !e.isCryptoError() {
return ""
}
return qtls.Alert(e - 0x100).Error()
}
func (e TransportErrorCode) String() string {
func (e ErrorCode) String() string {
switch e {
case NoError:
return "NO_ERROR"
@@ -69,7 +76,7 @@ func (e TransportErrorCode) String() string {
return "PROTOCOL_VIOLATION"
case InvalidToken:
return "INVALID_TOKEN"
case ApplicationErrorErrorCode:
case ApplicationError:
return "APPLICATION_ERROR"
case CryptoBufferExceeded:
return "CRYPTO_BUFFER_EXCEEDED"
@@ -80,7 +87,7 @@ func (e TransportErrorCode) String() string {
case NoViablePathError:
return "NO_VIABLE_PATH"
default:
if e.IsCryptoError() {
if e.isCryptoError() {
return fmt.Sprintf("CRYPTO_ERROR (%#x)", uint16(e))
}
return fmt.Sprintf("unknown error code: %#x", uint16(e))

View File

@@ -1,106 +0,0 @@
package qerr
import (
"fmt"
"net"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
var (
ErrHandshakeTimeout = &HandshakeTimeoutError{}
ErrIdleTimeout = &IdleTimeoutError{}
)
type TransportError struct {
Remote bool
FrameType uint64
ErrorCode TransportErrorCode
ErrorMessage string
}
var _ error = &TransportError{}
// NewCryptoError create a new TransportError instance for a crypto error
func NewCryptoError(tlsAlert uint8, errorMessage string) *TransportError {
return &TransportError{
ErrorCode: 0x100 + TransportErrorCode(tlsAlert),
ErrorMessage: errorMessage,
}
}
func (e *TransportError) Error() string {
str := e.ErrorCode.String()
if e.FrameType != 0 {
str += fmt.Sprintf(" (frame type: %#x)", e.FrameType)
}
msg := e.ErrorMessage
if len(msg) == 0 {
msg = e.ErrorCode.Message()
}
if len(msg) == 0 {
return str
}
return str + ": " + msg
}
// An ApplicationErrorCode is an application-defined error code.
type ApplicationErrorCode uint64
// A StreamErrorCode is an error code used to cancel streams.
type StreamErrorCode uint64
type ApplicationError struct {
Remote bool
ErrorCode ApplicationErrorCode
ErrorMessage string
}
var _ error = &ApplicationError{}
func (e *ApplicationError) Error() string {
if len(e.ErrorMessage) == 0 {
return fmt.Sprintf("Application error %#x", e.ErrorCode)
}
return fmt.Sprintf("Application error %#x: %s", e.ErrorCode, e.ErrorMessage)
}
type IdleTimeoutError struct{}
var _ error = &IdleTimeoutError{}
func (e *IdleTimeoutError) Timeout() bool { return true }
func (e *IdleTimeoutError) Temporary() bool { return false }
func (e *IdleTimeoutError) Error() string { return "timeout: no recent network activity" }
type HandshakeTimeoutError struct{}
var _ error = &HandshakeTimeoutError{}
func (e *HandshakeTimeoutError) Timeout() bool { return true }
func (e *HandshakeTimeoutError) Temporary() bool { return false }
func (e *HandshakeTimeoutError) Error() string { return "timeout: handshake did not complete in time" }
// A VersionNegotiationError occurs when the client and the server can't agree on a QUIC version.
type VersionNegotiationError struct {
Ours []protocol.VersionNumber
Theirs []protocol.VersionNumber
}
func (e *VersionNegotiationError) Error() string {
return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.Ours, e.Theirs)
}
// A StatelessResetError occurs when we receive a stateless reset.
type StatelessResetError struct {
Token protocol.StatelessResetToken
}
var _ net.Error = &StatelessResetError{}
func (e *StatelessResetError) Error() string {
return fmt.Sprintf("received a stateless reset with token %x", e.Token)
}
func (e *StatelessResetError) Timeout() bool { return false }
func (e *StatelessResetError) Temporary() bool { return true }

View File

@@ -1,55 +0,0 @@
// +build go1.16
package qerr
import (
"net"
)
func (e *TransportError) Is(target error) bool {
_, ok := target.(*TransportError)
if ok {
return true
}
return target == net.ErrClosed
}
func (e *ApplicationError) Is(target error) bool {
_, ok := target.(*ApplicationError)
if ok {
return true
}
return target == net.ErrClosed
}
func (e *IdleTimeoutError) Is(target error) bool {
_, ok := target.(*IdleTimeoutError)
if ok {
return true
}
return target == net.ErrClosed
}
func (e *HandshakeTimeoutError) Is(target error) bool {
_, ok := target.(*HandshakeTimeoutError)
if ok {
return true
}
return target == net.ErrClosed
}
func (e *VersionNegotiationError) Is(target error) bool {
_, ok := target.(*VersionNegotiationError)
if ok {
return true
}
return target == net.ErrClosed
}
func (e *StatelessResetError) Is(target error) bool {
_, ok := target.(*StatelessResetError)
if ok {
return true
}
return target == net.ErrClosed
}

View File

@@ -1,33 +0,0 @@
// +build !go1.16
package qerr
func (e *TransportError) Is(target error) bool {
_, ok := target.(*TransportError)
return ok
}
func (e *ApplicationError) Is(target error) bool {
_, ok := target.(*ApplicationError)
return ok
}
func (e *IdleTimeoutError) Is(target error) bool {
_, ok := target.(*IdleTimeoutError)
return ok
}
func (e *HandshakeTimeoutError) Is(target error) bool {
_, ok := target.(*HandshakeTimeoutError)
return ok
}
func (e *VersionNegotiationError) Is(target error) bool {
_, ok := target.(*VersionNegotiationError)
return ok
}
func (e *StatelessResetError) Is(target error) bool {
_, ok := target.(*StatelessResetError)
return ok
}

View File

@@ -0,0 +1,112 @@
package qerr
import (
"fmt"
"net"
)
// A QuicError consists of an error code plus a error reason
type QuicError struct {
ErrorCode ErrorCode
FrameType uint64 // only valid if this not an application error
ErrorMessage string
isTimeout bool
isApplicationError bool
}
var _ net.Error = &QuicError{}
// NewError creates a new QuicError instance
func NewError(errorCode ErrorCode, errorMessage string) *QuicError {
return &QuicError{
ErrorCode: errorCode,
ErrorMessage: errorMessage,
}
}
// NewErrorWithFrameType creates a new QuicError instance for a specific frame type
func NewErrorWithFrameType(errorCode ErrorCode, frameType uint64, errorMessage string) *QuicError {
return &QuicError{
ErrorCode: errorCode,
FrameType: frameType,
ErrorMessage: errorMessage,
}
}
// NewTimeoutError creates a new QuicError instance for a timeout error
func NewTimeoutError(errorMessage string) *QuicError {
return &QuicError{
ErrorMessage: errorMessage,
isTimeout: true,
}
}
// NewCryptoError create a new QuicError instance for a crypto error
func NewCryptoError(tlsAlert uint8, errorMessage string) *QuicError {
return &QuicError{
ErrorCode: 0x100 + ErrorCode(tlsAlert),
ErrorMessage: errorMessage,
}
}
// NewApplicationError creates a new QuicError instance for an application error
func NewApplicationError(errorCode ErrorCode, errorMessage string) *QuicError {
return &QuicError{
ErrorCode: errorCode,
ErrorMessage: errorMessage,
isApplicationError: true,
}
}
func (e *QuicError) Error() string {
if e.isApplicationError {
if len(e.ErrorMessage) == 0 {
return fmt.Sprintf("Application error %#x", uint64(e.ErrorCode))
}
return fmt.Sprintf("Application error %#x: %s", uint64(e.ErrorCode), e.ErrorMessage)
}
str := e.ErrorCode.String()
if e.FrameType != 0 {
str += fmt.Sprintf(" (frame type: %#x)", e.FrameType)
}
msg := e.ErrorMessage
if len(msg) == 0 {
msg = e.ErrorCode.Message()
}
if len(msg) == 0 {
return str
}
return str + ": " + msg
}
// IsCryptoError says if this error is a crypto error
func (e *QuicError) IsCryptoError() bool {
return e.ErrorCode.isCryptoError()
}
// IsApplicationError says if this error is an application error
func (e *QuicError) IsApplicationError() bool {
return e.isApplicationError
}
// Temporary says if the error is temporary.
func (e *QuicError) Temporary() bool {
return false
}
// Timeout says if this error is a timeout.
func (e *QuicError) Timeout() bool {
return e.isTimeout
}
// ToQuicError converts an arbitrary error to a QuicError. It leaves QuicErrors
// unchanged, and properly handles `ErrorCode`s.
func ToQuicError(err error) *QuicError {
switch e := err.(type) {
case *QuicError:
return e
case ErrorCode:
return NewError(e, "")
}
return NewError(InternalError, err.Error())
}

View File

@@ -10,7 +10,7 @@ import (
"net"
"unsafe"
"github.com/marten-seemann/qtls-go1-15"
qtls "github.com/marten-seemann/qtls-go1-15"
)
type (
@@ -53,6 +53,21 @@ const (
EncryptionApplication = qtls.EncryptionApplication
)
// CipherSuiteName gets the name of a cipher suite.
func CipherSuiteName(id uint16) string {
return qtls.CipherSuiteName(id)
}
// HkdfExtract generates a pseudorandom key for use with Expand from an input secret and an optional independent salt.
func HkdfExtract(hash crypto.Hash, newSecret, currentSecret []byte) []byte {
return qtls.HkdfExtract(hash, newSecret, currentSecret)
}
// HkdfExpandLabel HKDF expands a label
func HkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
return qtls.HkdfExpandLabel(hash, secret, hashValue, label, L)
}
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
return qtls.AEADAESGCMTLS13(key, fixedNonce)

View File

@@ -1,5 +1,4 @@
// +build go1.16
// +build !go1.17
package qtls
@@ -10,7 +9,7 @@ import (
"net"
"unsafe"
"github.com/marten-seemann/qtls-go1-16"
qtls "github.com/marten-seemann/qtls-go1-16"
)
type (
@@ -53,6 +52,21 @@ const (
EncryptionApplication = qtls.EncryptionApplication
)
// CipherSuiteName gets the name of a cipher suite.
func CipherSuiteName(id uint16) string {
return qtls.CipherSuiteName(id)
}
// HkdfExtract generates a pseudorandom key for use with Expand from an input secret and an optional independent salt.
func HkdfExtract(hash crypto.Hash, newSecret, currentSecret []byte) []byte {
return qtls.HkdfExtract(hash, newSecret, currentSecret)
}
// HkdfExpandLabel HKDF expands a label
func HkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
return qtls.HkdfExpandLabel(hash, secret, hashValue, label, L)
}
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
return qtls.AEADAESGCMTLS13(key, fixedNonce)

View File

@@ -1,99 +0,0 @@
// +build go1.17
package qtls
import (
"crypto"
"crypto/cipher"
"crypto/tls"
"net"
"unsafe"
"github.com/marten-seemann/qtls-go1-17"
)
type (
// Alert is a TLS alert
Alert = qtls.Alert
// A Certificate is qtls.Certificate.
Certificate = qtls.Certificate
// CertificateRequestInfo contains inforamtion about a certificate request.
CertificateRequestInfo = qtls.CertificateRequestInfo
// A CipherSuiteTLS13 is a cipher suite for TLS 1.3
CipherSuiteTLS13 = qtls.CipherSuiteTLS13
// ClientHelloInfo contains information about a ClientHello.
ClientHelloInfo = qtls.ClientHelloInfo
// ClientSessionCache is a cache used for session resumption.
ClientSessionCache = qtls.ClientSessionCache
// ClientSessionState is a state needed for session resumption.
ClientSessionState = qtls.ClientSessionState
// A Config is a qtls.Config.
Config = qtls.Config
// A Conn is a qtls.Conn.
Conn = qtls.Conn
// ConnectionState contains information about the state of the connection.
ConnectionState = qtls.ConnectionStateWith0RTT
// EncryptionLevel is the encryption level of a message.
EncryptionLevel = qtls.EncryptionLevel
// Extension is a TLS extension
Extension = qtls.Extension
// ExtraConfig is the qtls.ExtraConfig
ExtraConfig = qtls.ExtraConfig
// RecordLayer is a qtls RecordLayer.
RecordLayer = qtls.RecordLayer
)
const (
// EncryptionHandshake is the Handshake encryption level
EncryptionHandshake = qtls.EncryptionHandshake
// Encryption0RTT is the 0-RTT encryption level
Encryption0RTT = qtls.Encryption0RTT
// EncryptionApplication is the application data encryption level
EncryptionApplication = qtls.EncryptionApplication
)
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
return qtls.AEADAESGCMTLS13(key, fixedNonce)
}
// Client returns a new TLS client side connection.
func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
return qtls.Client(conn, config, extraConfig)
}
// Server returns a new TLS server side connection.
func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
return qtls.Server(conn, config, extraConfig)
}
func GetConnectionState(conn *Conn) ConnectionState {
return conn.ConnectionStateWith0RTT()
}
// ToTLSConnectionState extracts the tls.ConnectionState
func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState {
return cs.ConnectionState
}
type cipherSuiteTLS13 struct {
ID uint16
KeyLen int
AEAD func(key, fixedNonce []byte) cipher.AEAD
Hash crypto.Hash
}
//go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-17.cipherSuiteTLS13ByID
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite.
func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 {
val := cipherSuiteTLS13ByID(id)
cs := (*cipherSuiteTLS13)(unsafe.Pointer(val))
return &qtls.CipherSuiteTLS13{
ID: cs.ID,
KeyLen: cs.KeyLen,
AEAD: cs.AEAD,
Hash: cs.Hash,
}
}

View File

@@ -1,5 +0,0 @@
// +build go1.18
package qtls
var _ int = "quic-go doesn't build on Go 1.18 yet."

View File

@@ -5,13 +5,14 @@ import (
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/quicvarint"
)
// A ConnectionCloseFrame is a CONNECTION_CLOSE frame
type ConnectionCloseFrame struct {
IsApplicationError bool
ErrorCode uint64
ErrorCode qerr.ErrorCode
FrameType uint64
ReasonPhrase string
}
@@ -27,7 +28,7 @@ func parseConnectionCloseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*Conn
if err != nil {
return nil, err
}
f.ErrorCode = ec
f.ErrorCode = qerr.ErrorCode(ec)
// read the Frame Type, if this is not an application error
if !f.IsApplicationError {
ft, err := quicvarint.Read(r)
@@ -58,8 +59,8 @@ func parseConnectionCloseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*Conn
}
// Length of a written frame
func (f *ConnectionCloseFrame) Length(protocol.VersionNumber) protocol.ByteCount {
length := 1 + quicvarint.Len(f.ErrorCode) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
func (f *ConnectionCloseFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
length := 1 + quicvarint.Len(uint64(f.ErrorCode)) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
if !f.IsApplicationError {
length += quicvarint.Len(f.FrameType) // for the frame type
}
@@ -73,7 +74,7 @@ func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNu
b.WriteByte(0x1c)
}
quicvarint.Write(b, f.ErrorCode)
quicvarint.Write(b, uint64(f.ErrorCode))
if !f.IsApplicationError {
quicvarint.Write(b, f.FrameType)
}

View File

@@ -26,7 +26,7 @@ func NewFrameParser(supportsDatagrams bool, v protocol.VersionNumber) FrameParse
}
}
// ParseNext parses the next frame.
// ParseNextFrame parses the next frame
// It skips PADDING frames.
func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel) (Frame, error) {
for r.Len() != 0 {
@@ -38,11 +38,7 @@ func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLev
f, err := p.parseFrame(r, typeByte, encLevel)
if err != nil {
return nil, &qerr.TransportError{
FrameType: uint64(typeByte),
ErrorCode: qerr.FrameEncodingError,
ErrorMessage: err.Error(),
}
return nil, qerr.NewErrorWithFrameType(qerr.FrameEncodingError, uint64(typeByte), err.Error())
}
return f, nil
}

View File

@@ -4,14 +4,13 @@ import (
"bytes"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/quicvarint"
)
// A ResetStreamFrame is a RESET_STREAM frame in QUIC
type ResetStreamFrame struct {
StreamID protocol.StreamID
ErrorCode qerr.StreamErrorCode
ErrorCode protocol.ApplicationErrorCode
FinalSize protocol.ByteCount
}
@@ -39,7 +38,7 @@ func parseResetStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*ResetStr
return &ResetStreamFrame{
StreamID: streamID,
ErrorCode: qerr.StreamErrorCode(errorCode),
ErrorCode: protocol.ApplicationErrorCode(errorCode),
FinalSize: byteOffset,
}, nil
}

View File

@@ -4,14 +4,13 @@ import (
"bytes"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/quicvarint"
)
// A StopSendingFrame is a STOP_SENDING frame
type StopSendingFrame struct {
StreamID protocol.StreamID
ErrorCode qerr.StreamErrorCode
ErrorCode protocol.ApplicationErrorCode
}
// parseStopSendingFrame parses a STOP_SENDING frame
@@ -31,7 +30,7 @@ func parseStopSendingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StopSend
return &StopSendingFrame{
StreamID: protocol.StreamID(streamID),
ErrorCode: qerr.StreamErrorCode(errorCode),
ErrorCode: protocol.ApplicationErrorCode(errorCode),
}, nil
}

View File

@@ -6,6 +6,7 @@ import (
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/quicvarint"
)
@@ -78,7 +79,7 @@ func parseStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamFrame,
}
}
if frame.Offset+frame.DataLen() > protocol.MaxByteCount {
return nil, errors.New("stream data overflows maximum offset")
return nil, qerr.NewError(qerr.FrameEncodingError, "stream data overflows maximum offset")
}
return frame, nil
}

View File

@@ -90,10 +90,7 @@ type TransportParameters struct {
// Unmarshal the transport parameters
func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error {
if err := p.unmarshal(bytes.NewReader(data), sentBy, false); err != nil {
return &qerr.TransportError{
ErrorCode: qerr.TransportParameterError,
ErrorMessage: err.Error(),
}
return qerr.NewError(qerr.TransportParameterError, err.Error())
}
return nil
}
@@ -262,7 +259,7 @@ func (p *TransportParameters) readNumericTransportParameter(
return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err)
}
if remainingLen-r.Len() != expectedLen {
return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", paramID)
return fmt.Errorf("inconsistent transport parameter length for %d", paramID)
}
//nolint:exhaustive // This only covers the numeric transport parameters.
switch paramID {

View File

@@ -0,0 +1,78 @@
package logging
// A CloseReason is the reason why a QUIC connection is closed.
// It falls in one of 4 categories:
// 1. The application closed the connection (with an application-specific error code).
// 2. The transport closed the connection with a transport-error code.
// 3. The connection timed out, either during the handshake, or due to an idle timeout.
// 4. A stateless reset was received.
type CloseReason struct {
remote bool
applicationError *ApplicationError
transportError *TransportError
timeout *TimeoutReason
statelessResetToken *StatelessResetToken
versions []VersionNumber
}
// NewApplicationCloseReason creates a new CloseReason for an application error.
func NewApplicationCloseReason(errorCode ApplicationError, remote bool) CloseReason {
return CloseReason{remote: remote, applicationError: &errorCode}
}
// NewTransportCloseReason creates a new CloseReason for a transport error.
func NewTransportCloseReason(errorCode TransportError, remote bool) CloseReason {
return CloseReason{remote: remote, transportError: &errorCode}
}
// NewTimeoutCloseReason creates a new CloseReason for a connection timeout.
func NewTimeoutCloseReason(r TimeoutReason) CloseReason {
return CloseReason{timeout: &r}
}
// NewStatelessResetCloseReason creates a new CloseReason for a stateless reset.
func NewStatelessResetCloseReason(token StatelessResetToken) CloseReason {
return CloseReason{statelessResetToken: &token}
}
// NewVersionNegotiationError creates a new CloseReason for a version negotiation error.
func NewVersionNegotiationError(versions []VersionNumber) CloseReason {
return CloseReason{versions: versions}
}
// ApplicationError gets the application error.
func (r *CloseReason) ApplicationError() (errorCode ApplicationError, remote bool, ok bool) {
if r.applicationError == nil {
return
}
return *r.applicationError, r.remote, true
}
// TransportError gets the transport error.
func (r *CloseReason) TransportError() (errorCode TransportError, remote bool, ok bool) {
if r.transportError == nil {
return
}
return *r.transportError, r.remote, true
}
// Timeout gets the timeout error.
func (r *CloseReason) Timeout() (reason TimeoutReason, ok bool) {
if r.timeout == nil {
return
}
return *r.timeout, true
}
// StatelessReset gets the stateless reset token.
func (r *CloseReason) StatelessReset() (token StatelessResetToken, ok bool) {
if r.statelessResetToken == nil {
return
}
return *r.statelessResetToken, true
}
func (r *CloseReason) VersionNegotiation() (versions []VersionNumber, ok bool) {
return r.versions, len(r.versions) > 0
}

View File

@@ -3,7 +3,6 @@
package logging
import (
"context"
"net"
"time"
@@ -50,9 +49,9 @@ type (
PreferredAddress = wire.PreferredAddress
// A TransportError is a transport-level error code.
TransportError = qerr.TransportErrorCode
TransportError = qerr.ErrorCode
// An ApplicationError is an application-defined error code.
ApplicationError = qerr.TransportErrorCode
ApplicationError = qerr.ErrorCode
// The RTTStats contain statistics used by the congestion controller.
RTTStats = utils.RTTStats
@@ -92,11 +91,11 @@ const (
// A Tracer traces events.
type Tracer interface {
// TracerForConnection requests a new tracer for a connection.
// ConnectionTracer requests a new tracer for a connection.
// The ODCID is the original destination connection ID:
// The destination connection ID that the client used on the first Initial packet it sent on this connection.
// If nil is returned, tracing will be disabled for this connection.
TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer
TracerForConnection(p Perspective, odcid ConnectionID) ConnectionTracer
SentPacket(net.Addr, *Header, ByteCount, []Frame)
DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason)
@@ -104,9 +103,8 @@ type Tracer interface {
// A ConnectionTracer records events.
type ConnectionTracer interface {
StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID)
NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber)
ClosedConnection(error)
StartedConnection(local, remote net.Addr, version VersionNumber, srcConnID, destConnID ConnectionID)
ClosedConnection(CloseReason)
SentTransportParameters(*TransportParameters)
ReceivedTransportParameters(*TransportParameters)
RestoredTransportParameters(parameters *TransportParameters) // for 0-RTT
@@ -117,7 +115,6 @@ type ConnectionTracer interface {
BufferedPacket(PacketType)
DroppedPacket(PacketType, ByteCount, PacketDropReason)
UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int)
AcknowledgedPacket(EncryptionLevel, PacketNumber)
LostPacket(EncryptionLevel, PacketNumber, PacketLossReason)
UpdatedCongestionState(CongestionState)
UpdatedPTOCount(value uint32)

View File

@@ -1,7 +1,6 @@
package logging
import (
"context"
"net"
"time"
)
@@ -23,10 +22,10 @@ func NewMultiplexedTracer(tracers ...Tracer) Tracer {
return &tracerMultiplexer{tracers}
}
func (m *tracerMultiplexer) TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer {
func (m *tracerMultiplexer) TracerForConnection(p Perspective, odcid ConnectionID) ConnectionTracer {
var connTracers []ConnectionTracer
for _, t := range m.tracers {
if ct := t.TracerForConnection(ctx, p, odcid); ct != nil {
if ct := t.TracerForConnection(p, odcid); ct != nil {
connTracers = append(connTracers, ct)
}
}
@@ -62,21 +61,15 @@ func NewMultiplexedConnectionTracer(tracers ...ConnectionTracer) ConnectionTrace
return &connTracerMultiplexer{tracers: tracers}
}
func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) {
func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, version VersionNumber, srcConnID, destConnID ConnectionID) {
for _, t := range m.tracers {
t.StartedConnection(local, remote, srcConnID, destConnID)
t.StartedConnection(local, remote, version, srcConnID, destConnID)
}
}
func (m *connTracerMultiplexer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) {
func (m *connTracerMultiplexer) ClosedConnection(reason CloseReason) {
for _, t := range m.tracers {
t.NegotiatedVersion(chosen, clientVersions, serverVersions)
}
}
func (m *connTracerMultiplexer) ClosedConnection(e error) {
for _, t := range m.tracers {
t.ClosedConnection(e)
t.ClosedConnection(reason)
}
}
@@ -146,12 +139,6 @@ func (m *connTracerMultiplexer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesIn
}
}
func (m *connTracerMultiplexer) AcknowledgedPacket(encLevel EncryptionLevel, pn PacketNumber) {
for _, t := range m.tracers {
t.AcknowledgedPacket(encLevel, pn)
}
}
func (m *connTracerMultiplexer) LostPacket(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) {
for _, t := range m.tracers {
t.LostPacket(encLevel, pn, reason)

View File

@@ -1,7 +1,9 @@
package logging
import "github.com/lucas-clemente/quic-go/internal/protocol"
// PacketType is the packet type of a QUIC packet
type PacketType uint8
type PacketType = protocol.PacketType
const (
// PacketTypeInitial is the packet type of an Initial packet

View File

@@ -18,6 +18,14 @@ import (
"github.com/lucas-clemente/quic-go/logging"
)
type statelessResetErr struct {
token protocol.StatelessResetToken
}
func (e statelessResetErr) Error() string {
return fmt.Sprintf("received a stateless reset with token %x", e.token)
}
type zeroRTTQueue struct {
queue []*receivedPacket
retireTimer *time.Timer
@@ -422,7 +430,7 @@ func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool {
copy(token[:], data[len(data)-16:])
if sess, ok := h.resetTokens[token]; ok {
h.logger.Debugf("Received a stateless reset with token %#x. Closing session.", token)
go sess.destroy(&StatelessResetError{Token: token})
go sess.destroy(statelessResetErr{token: token})
return true
}
return false

View File

@@ -20,8 +20,7 @@ type packer interface {
PackPacket() (*packedPacket, error)
MaybePackProbePacket(protocol.EncryptionLevel) (*packedPacket, error)
MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error)
PackConnectionClose(*qerr.TransportError) (*coalescedPacket, error)
PackApplicationClose(*qerr.ApplicationError) (*coalescedPacket, error)
PackConnectionClose(*qerr.QuicError) (*coalescedPacket, error)
SetMaxPacketSize(protocol.ByteCount)
PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount) (*packedPacket, error)
@@ -204,27 +203,14 @@ func newPacketPacker(
}
}
// PackConnectionClose packs a packet that closes the connection with a transport error.
func (p *packetPacker) PackConnectionClose(e *qerr.TransportError) (*coalescedPacket, error) {
// PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame
func (p *packetPacker) PackConnectionClose(quicErr *qerr.QuicError) (*coalescedPacket, error) {
var reason string
// don't send details of crypto errors
if !e.ErrorCode.IsCryptoError() {
reason = e.ErrorMessage
if !quicErr.IsCryptoError() {
reason = quicErr.ErrorMessage
}
return p.packConnectionClose(false, uint64(e.ErrorCode), e.FrameType, reason)
}
// PackApplicationClose packs a packet that closes the connection with an application error.
func (p *packetPacker) PackApplicationClose(e *qerr.ApplicationError) (*coalescedPacket, error) {
return p.packConnectionClose(true, uint64(e.ErrorCode), 0, e.ErrorMessage)
}
func (p *packetPacker) packConnectionClose(
isApplicationError bool,
errorCode uint64,
frameType uint64,
reason string,
) (*coalescedPacket, error) {
var sealers [4]sealer
var hdrs [4]*wire.ExtendedHeader
var payloads [4]*payload
@@ -235,17 +221,20 @@ func (p *packetPacker) packConnectionClose(
if p.perspective == protocol.PerspectiveServer && encLevel == protocol.Encryption0RTT {
continue
}
ccf := &wire.ConnectionCloseFrame{
IsApplicationError: isApplicationError,
ErrorCode: errorCode,
FrameType: frameType,
ReasonPhrase: reason,
quicErrToSend := quicErr
reasonPhrase := reason
if encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake {
// don't send application errors in Initial or Handshake packets
if quicErr.IsApplicationError() {
quicErrToSend = qerr.NewError(qerr.ApplicationError, "")
reasonPhrase = ""
}
}
// don't send application errors in Initial or Handshake packets
if isApplicationError && (encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake) {
ccf.IsApplicationError = false
ccf.ErrorCode = uint64(qerr.ApplicationErrorErrorCode)
ccf.ReasonPhrase = ""
ccf := &wire.ConnectionCloseFrame{
IsApplicationError: quicErrToSend.IsApplicationError(),
ErrorCode: quicErrToSend.ErrorCode,
FrameType: quicErrToSend.FrameType,
ReasonPhrase: reasonPhrase,
}
payload := &payload{
frames: []ackhandler.Frame{{Frame: ccf}},

View File

@@ -8,7 +8,6 @@ import (
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
)
@@ -39,7 +38,7 @@ type receiveStream struct {
closeForShutdownErr error
cancelReadErr error
resetRemotelyErr *StreamError
resetRemotelyErr StreamError
closedForShutdown bool // set when CloseForShutdown() is called
finRead bool // set once we read a frame with a Fin
@@ -197,7 +196,7 @@ func (s *receiveStream) dequeueNextFrame() {
s.readPosInFrame = 0
}
func (s *receiveStream) CancelRead(errorCode StreamErrorCode) {
func (s *receiveStream) CancelRead(errorCode protocol.ApplicationErrorCode) {
s.mutex.Lock()
completed := s.cancelReadImpl(errorCode)
s.mutex.Unlock()
@@ -208,7 +207,7 @@ func (s *receiveStream) CancelRead(errorCode StreamErrorCode) {
}
}
func (s *receiveStream) cancelReadImpl(errorCode qerr.StreamErrorCode) bool /* completed */ {
func (s *receiveStream) cancelReadImpl(errorCode protocol.ApplicationErrorCode) bool /* completed */ {
if s.finRead || s.canceledRead || s.resetRemotely {
return false
}
@@ -282,9 +281,9 @@ func (s *receiveStream) handleResetStreamFrameImpl(frame *wire.ResetStreamFrame)
return false, nil
}
s.resetRemotely = true
s.resetRemotelyErr = &StreamError{
StreamID: s.streamID,
ErrorCode: frame.ErrorCode,
s.resetRemotelyErr = streamCanceledError{
errorCode: frame.ErrorCode,
error: fmt.Errorf("stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
}
s.signalRead()
return newlyRcvdFinalOffset, nil

View File

@@ -7,9 +7,9 @@ import (
"time"
"github.com/lucas-clemente/quic-go/internal/ackhandler"
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
)
@@ -407,12 +407,12 @@ func (s *sendStream) Close() error {
return nil
}
func (s *sendStream) CancelWrite(errorCode StreamErrorCode) {
func (s *sendStream) CancelWrite(errorCode protocol.ApplicationErrorCode) {
s.cancelWriteImpl(errorCode, fmt.Errorf("Write on stream %d canceled with error code %d", s.streamID, errorCode))
}
// must be called after locking the mutex
func (s *sendStream) cancelWriteImpl(errorCode qerr.StreamErrorCode, writeErr error) {
func (s *sendStream) cancelWriteImpl(errorCode protocol.ApplicationErrorCode, writeErr error) {
s.mutex.Lock()
if s.canceledWrite {
s.mutex.Unlock()
@@ -449,10 +449,11 @@ func (s *sendStream) updateSendWindow(limit protocol.ByteCount) {
}
func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) {
s.cancelWriteImpl(frame.ErrorCode, &StreamError{
StreamID: s.streamID,
ErrorCode: frame.ErrorCode,
})
writeErr := streamCanceledError{
errorCode: frame.ErrorCode,
error: fmt.Errorf("stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
}
s.cancelWriteImpl(frame.ErrorCode, writeErr)
}
func (s *sendStream) Context() context.Context {

View File

@@ -87,7 +87,6 @@ type baseServer struct {
*handshake.TokenGenerator,
bool, /* enable 0-RTT */
logging.ConnectionTracer,
uint64,
utils.Logger,
protocol.VersionNumber,
) quicSession
@@ -451,7 +450,6 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
}
s.logger.Debugf("Changing connection ID to %s.", connID)
var sess quicSession
tracingID := nextSessionTracingID()
if added := s.sessionHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler {
var tracer logging.ConnectionTracer
if s.config.Tracer != nil {
@@ -460,11 +458,7 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
if origDestConnID.Len() > 0 {
connID = origDestConnID
}
tracer = s.config.Tracer.TracerForConnection(
context.WithValue(context.Background(), SessionTracingKey, tracingID),
protocol.PerspectiveServer,
connID,
)
tracer = s.config.Tracer.TracerForConnection(protocol.PerspectiveServer, connID)
}
sess = s.newSession(
newSendConn(s.conn, p.remoteAddr, p.info),
@@ -480,7 +474,6 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
s.tokenGenerator,
s.acceptEarlySessions,
tracer,
tracingID,
s.logger,
hdr.Version,
)
@@ -600,12 +593,12 @@ func (s *baseServer) sendConnectionRefused(remoteAddr net.Addr, hdr *wire.Header
}
// sendError sends the error as a response to the packet received with header hdr
func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.TransportErrorCode, info *packetInfo) error {
func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.ErrorCode, info *packetInfo) error {
packetBuffer := getPacketBuffer()
defer packetBuffer.Release()
buf := bytes.NewBuffer(packetBuffer.Data)
ccf := &wire.ConnectionCloseFrame{ErrorCode: uint64(errorCode)}
ccf := &wire.ConnectionCloseFrame{ErrorCode: errorCode}
replyHdr := &wire.ExtendedHeader{}
replyHdr.IsLongHeader = true

View File

@@ -10,7 +10,6 @@ import (
"net"
"reflect"
"sync"
"sync/atomic"
"time"
"github.com/lucas-clemente/quic-go/internal/ackhandler"
@@ -123,17 +122,23 @@ type errCloseForRecreating struct {
nextVersion protocol.VersionNumber
}
func (e *errCloseForRecreating) Error() string {
func (errCloseForRecreating) Error() string {
return "closing session in order to recreate it"
}
func (e *errCloseForRecreating) Is(target error) bool {
_, ok := target.(*errCloseForRecreating)
func (errCloseForRecreating) Is(target error) bool {
_, ok := target.(errCloseForRecreating)
return ok
}
var sessionTracingID uint64 // to be accessed atomically
func nextSessionTracingID() uint64 { return atomic.AddUint64(&sessionTracingID, 1) }
type errVersionNegotiation struct {
ourVersions []protocol.VersionNumber
theirVersions []protocol.VersionNumber
}
func (e errVersionNegotiation) Error() string {
return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.ourVersions, e.theirVersions)
}
// A Session is a QUIC session
type session struct {
@@ -247,7 +252,6 @@ var newSession = func(
tokenGenerator *handshake.TokenGenerator,
enable0RTT bool,
tracer logging.ConnectionTracer,
tracingID uint64,
logger utils.Logger,
v protocol.VersionNumber,
) quicSession {
@@ -287,10 +291,8 @@ var newSession = func(
s.version,
)
s.preSetup()
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID))
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
0,
getMaxPacketSize(s.conn.RemoteAddr()),
s.rttStats,
s.perspective,
s.tracer,
@@ -378,7 +380,6 @@ var newClientSession = func(
enable0RTT bool,
hasNegotiatedVersion bool,
tracer logging.ConnectionTracer,
tracingID uint64,
logger utils.Logger,
v protocol.VersionNumber,
) quicSession {
@@ -414,10 +415,8 @@ var newClientSession = func(
s.version,
)
s.preSetup()
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID))
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
initialPacketNumber,
getMaxPacketSize(s.conn.RemoteAddr()),
s.rttStats,
s.perspective,
s.tracer,
@@ -523,6 +522,7 @@ func (s *session) preSetup() {
s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets)
s.closeChan = make(chan closeError, 1)
s.sendingScheduled = make(chan struct{}, 1)
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background())
now := time.Now()
@@ -608,6 +608,7 @@ runLoop:
// nothing to see here.
case <-sendQueueAvailable:
case firstPacket := <-s.receivedPackets:
s.sentPacketHandler.ReceivedBytes(firstPacket.Size())
wasProcessed := s.handlePacketImpl(firstPacket)
// Don't set timers and send packets if the packet made us close the session.
select {
@@ -663,13 +664,19 @@ runLoop:
s.framer.QueueControlFrame(&wire.PingFrame{})
s.keepAlivePingSent = true
} else if !s.handshakeComplete && now.Sub(s.sessionCreationTime) >= s.config.handshakeTimeout() {
s.destroyImpl(qerr.ErrHandshakeTimeout)
if s.tracer != nil {
s.tracer.ClosedConnection(logging.NewTimeoutCloseReason(logging.TimeoutReasonHandshake))
}
s.destroyImpl(qerr.NewTimeoutError("Handshake did not complete in time"))
continue
} else {
idleTimeoutStartTime := s.idleTimeoutStartTime()
if (!s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.config.HandshakeIdleTimeout) ||
(s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.idleTimeout) {
s.destroyImpl(qerr.ErrIdleTimeout)
if s.tracer != nil {
s.tracer.ClosedConnection(logging.NewTimeoutCloseReason(logging.TimeoutReasonIdle))
}
s.destroyImpl(qerr.NewTimeoutError("No recent network activity"))
continue
}
}
@@ -690,8 +697,8 @@ runLoop:
}
}
s.handleCloseError(&closeErr)
if !errors.Is(closeErr.err, &errCloseForRecreating{}) && s.tracer != nil {
s.handleCloseError(closeErr)
if !errors.Is(closeErr.err, errCloseForRecreating{}) && s.tracer != nil {
s.tracer.Close()
}
s.logger.Infof("Connection %s closed.", s.logID)
@@ -731,26 +738,23 @@ func (s *session) nextKeepAliveTime() time.Time {
if !s.config.KeepAlive || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() {
return time.Time{}
}
return s.lastPacketReceivedTime.Add(s.keepAliveInterval)
return s.lastPacketReceivedTime.Add(s.keepAliveInterval / 2)
}
func (s *session) maybeResetTimer() {
var deadline time.Time
if !s.handshakeComplete {
deadline = utils.MinTime(
s.sessionCreationTime.Add(s.config.handshakeTimeout()),
s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout),
)
deadline = s.sessionCreationTime.Add(utils.MinDuration(s.config.handshakeTimeout(), s.config.HandshakeIdleTimeout))
} else {
if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() {
deadline = keepAliveTime
} else {
deadline = s.idleTimeoutStartTime().Add(s.idleTimeout)
}
}
if s.handshakeConfirmed && !s.config.DisablePathMTUDiscovery {
if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() {
deadline = utils.MinTime(deadline, probeTime)
if !s.config.DisablePathMTUDiscovery {
if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() {
deadline = utils.MinTime(deadline, probeTime)
}
}
}
@@ -782,36 +786,6 @@ func (s *session) handleHandshakeComplete() {
s.connIDManager.SetHandshakeComplete()
s.connIDGenerator.SetHandshakeComplete()
if s.perspective == protocol.PerspectiveClient {
s.applyTransportParameters()
return
}
s.handleHandshakeConfirmed()
ticket, err := s.cryptoStreamHandler.GetSessionTicket()
if err != nil {
s.closeLocal(err)
}
if ticket != nil {
s.oneRTTStream.Write(ticket)
for s.oneRTTStream.HasData() {
s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
}
}
token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr())
if err != nil {
s.closeLocal(err)
}
s.queueControlFrame(&wire.NewTokenFrame{Token: token})
s.queueControlFrame(&wire.HandshakeDoneFrame{})
}
func (s *session) handleHandshakeConfirmed() {
s.handshakeConfirmed = true
s.sentPacketHandler.SetHandshakeConfirmed()
s.cryptoStreamHandler.SetHandshakeConfirmed()
if !s.config.DisablePathMTUDiscovery {
maxPacketSize := s.peerParams.MaxUDPPayloadSize
if maxPacketSize == 0 {
@@ -828,11 +802,34 @@ func (s *session) handleHandshakeConfirmed() {
},
)
}
if s.perspective == protocol.PerspectiveClient {
s.applyTransportParameters()
return
}
s.handshakeConfirmed = true
s.sentPacketHandler.SetHandshakeConfirmed()
ticket, err := s.cryptoStreamHandler.GetSessionTicket()
if err != nil {
s.closeLocal(err)
}
if ticket != nil {
s.oneRTTStream.Write(ticket)
for s.oneRTTStream.HasData() {
s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
}
}
token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr())
if err != nil {
s.closeLocal(err)
}
s.queueControlFrame(&wire.NewTokenFrame{Token: token})
s.cryptoStreamHandler.SetHandshakeConfirmed()
s.queueControlFrame(&wire.HandshakeDoneFrame{})
}
func (s *session) handlePacketImpl(rp *receivedPacket) bool {
s.sentPacketHandler.ReceivedBytes(rp.Size())
if wire.IsVersionNegotiationPacket(rp.data) {
s.handleVersionNegotiationPacket(rp)
return false
@@ -943,10 +940,7 @@ func (s *session) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /
wasQueued = true
s.tryQueueingUndecryptablePacket(p, hdr)
case wire.ErrInvalidReservedBits:
s.closeLocal(&qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: err.Error(),
})
s.closeLocal(qerr.NewError(qerr.ProtocolViolation, err.Error()))
case handshake.ErrDecryptionFailed:
// This might be a packet injected by an attacker. Drop it.
if s.tracer != nil {
@@ -1087,16 +1081,13 @@ func (s *session) handleVersionNegotiationPacket(p *receivedPacket) {
}
newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions)
if !ok {
s.destroyImpl(&VersionNegotiationError{
Ours: s.config.Versions,
Theirs: supportedVersions,
s.destroyImpl(errVersionNegotiation{
ourVersions: s.config.Versions,
theirVersions: supportedVersions,
})
s.logger.Infof("No compatible QUIC version found.")
return
}
if s.tracer != nil {
s.tracer.NegotiatedVersion(newVersion, s.config.Versions, supportedVersions)
}
s.logger.Infof("Switching to QUIC version %s.", newVersion)
nextPN, _ := s.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial)
@@ -1113,24 +1104,11 @@ func (s *session) handleUnpackedPacket(
packetSize protocol.ByteCount, // only for logging
) error {
if len(packet.data) == 0 {
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: "empty packet",
}
return qerr.NewError(qerr.ProtocolViolation, "empty packet")
}
if !s.receivedFirstPacket {
s.receivedFirstPacket = true
if !s.versionNegotiated && s.tracer != nil {
var clientVersions, serverVersions []protocol.VersionNumber
switch s.perspective {
case protocol.PerspectiveClient:
clientVersions = s.config.Versions
case protocol.PerspectiveServer:
serverVersions = s.config.Versions
}
s.tracer.NegotiatedVersion(s.version, clientVersions, serverVersions)
}
// The server can change the source connection ID with the first Handshake packet.
if s.perspective == protocol.PerspectiveClient && packet.hdr.IsLongHeader && !packet.hdr.SrcConnectionID.Equal(s.handshakeDestConnID) {
cid := packet.hdr.SrcConnectionID
@@ -1152,6 +1130,7 @@ func (s *session) handleUnpackedPacket(
s.tracer.StartedConnection(
s.conn.LocalAddr(),
s.conn.RemoteAddr(),
s.version,
packet.hdr.SrcConnectionID,
packet.hdr.DestConnectionID,
)
@@ -1267,20 +1246,13 @@ func (s *session) handlePacket(p *receivedPacket) {
}
func (s *session) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) {
var e error
if frame.IsApplicationError {
s.closeRemote(&qerr.ApplicationError{
Remote: true,
ErrorCode: qerr.ApplicationErrorCode(frame.ErrorCode),
ErrorMessage: frame.ReasonPhrase,
})
return
e = qerr.NewApplicationError(frame.ErrorCode, frame.ReasonPhrase)
} else {
e = qerr.NewError(frame.ErrorCode, frame.ReasonPhrase)
}
s.closeRemote(&qerr.TransportError{
Remote: true,
ErrorCode: qerr.TransportErrorCode(frame.ErrorCode),
FrameType: frame.FrameType,
ErrorMessage: frame.ReasonPhrase,
})
s.closeRemote(e)
}
func (s *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error {
@@ -1361,10 +1333,7 @@ func (s *session) handlePathChallengeFrame(frame *wire.PathChallengeFrame) {
func (s *session) handleNewTokenFrame(frame *wire.NewTokenFrame) error {
if s.perspective == protocol.PerspectiveServer {
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: "received NEW_TOKEN frame from the client",
}
return qerr.NewError(qerr.ProtocolViolation, "Received NEW_TOKEN frame from the client.")
}
if s.config.TokenStore != nil {
s.config.TokenStore.Put(s.tokenStoreKey, &ClientToken{data: frame.Token})
@@ -1382,37 +1351,27 @@ func (s *session) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame,
func (s *session) handleHandshakeDoneFrame() error {
if s.perspective == protocol.PerspectiveServer {
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: "received a HANDSHAKE_DONE frame",
}
}
if !s.handshakeConfirmed {
s.handleHandshakeConfirmed()
return qerr.NewError(qerr.ProtocolViolation, "received a HANDSHAKE_DONE frame")
}
s.handshakeConfirmed = true
s.sentPacketHandler.SetHandshakeConfirmed()
s.cryptoStreamHandler.SetHandshakeConfirmed()
return nil
}
func (s *session) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error {
acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime)
if err != nil {
if err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime); err != nil {
return err
}
if !acked1RTTPacket {
if encLevel != protocol.Encryption1RTT {
return nil
}
if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed {
s.handleHandshakeConfirmed()
}
return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked())
}
func (s *session) handleDatagramFrame(f *wire.DatagramFrame) error {
if f.Length(s.version) > protocol.MaxDatagramFrameSize {
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: "DATAGRAM frame too large",
}
return qerr.NewError(qerr.ProtocolViolation, "DATAGRAM frame too large")
}
s.datagramQueue.HandleDatagramFrame(f)
return nil
@@ -1461,48 +1420,44 @@ func (s *session) shutdown() {
<-s.ctx.Done()
}
func (s *session) CloseWithError(code ApplicationErrorCode, desc string) error {
s.closeLocal(&qerr.ApplicationError{
ErrorCode: code,
ErrorMessage: desc,
})
func (s *session) CloseWithError(code protocol.ApplicationErrorCode, desc string) error {
s.closeLocal(qerr.NewApplicationError(qerr.ErrorCode(code), desc))
<-s.ctx.Done()
return nil
}
func (s *session) handleCloseError(closeErr *closeError) {
e := closeErr.err
if e == nil {
e = &qerr.ApplicationError{}
} else {
defer func() {
closeErr.err = e
}()
func (s *session) handleCloseError(closeErr closeError) {
if closeErr.err == nil {
closeErr.err = qerr.NewApplicationError(0, "")
}
switch {
case errors.Is(e, qerr.ErrIdleTimeout),
errors.Is(e, qerr.ErrHandshakeTimeout),
errors.Is(e, &StatelessResetError{}),
errors.Is(e, &VersionNegotiationError{}),
errors.Is(e, &errCloseForRecreating{}),
errors.Is(e, &qerr.ApplicationError{}),
errors.Is(e, &qerr.TransportError{}):
default:
e = &qerr.TransportError{
ErrorCode: qerr.InternalError,
ErrorMessage: e.Error(),
}
var quicErr *qerr.QuicError
var ok bool
if quicErr, ok = closeErr.err.(*qerr.QuicError); !ok {
quicErr = qerr.ToQuicError(closeErr.err)
}
s.streamsMap.CloseWithError(e)
s.streamsMap.CloseWithError(quicErr)
s.connIDManager.Close()
if s.datagramQueue != nil {
s.datagramQueue.CloseWithError(e)
s.datagramQueue.CloseWithError(quicErr)
}
if s.tracer != nil && !errors.Is(e, &errCloseForRecreating{}) {
s.tracer.ClosedConnection(e)
if s.tracer != nil {
// timeout errors are logged as soon as they occur (to distinguish between handshake and idle timeouts)
if nerr, ok := closeErr.err.(net.Error); !ok || !nerr.Timeout() {
var resetErr statelessResetErr
var vnErr errVersionNegotiation
if errors.As(closeErr.err, &resetErr) {
s.tracer.ClosedConnection(logging.NewStatelessResetCloseReason(resetErr.token))
} else if errors.As(closeErr.err, &vnErr) {
s.tracer.ClosedConnection(logging.NewVersionNegotiationError(vnErr.theirVersions))
} else if quicErr.IsApplicationError() {
s.tracer.ClosedConnection(logging.NewApplicationCloseReason(quicErr.ErrorCode, closeErr.remote))
} else {
s.tracer.ClosedConnection(logging.NewTransportCloseReason(quicErr.ErrorCode, closeErr.remote))
}
}
}
// If this is a remote close we're done here
@@ -1514,7 +1469,7 @@ func (s *session) handleCloseError(closeErr *closeError) {
s.connIDGenerator.RemoveAll()
return
}
connClosePacket, err := s.sendConnectionClose(e)
connClosePacket, err := s.sendConnectionClose(quicErr)
if err != nil {
s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err)
}
@@ -1553,10 +1508,7 @@ func (s *session) restoreTransportParameters(params *wire.TransportParameters) {
func (s *session) handleTransportParameters(params *wire.TransportParameters) {
if err := s.checkTransportParameters(params); err != nil {
s.closeLocal(&qerr.TransportError{
ErrorCode: qerr.TransportParameterError,
ErrorMessage: err.Error(),
})
s.closeLocal(err)
}
s.peerParams = params
// On the client side we have to wait for handshake completion.
@@ -1579,7 +1531,7 @@ func (s *session) checkTransportParameters(params *wire.TransportParameters) err
// check the initial_source_connection_id
if !params.InitialSourceConnectionID.Equal(s.handshakeDestConnID) {
return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID)
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID))
}
if s.perspective == protocol.PerspectiveServer {
@@ -1587,17 +1539,17 @@ func (s *session) checkTransportParameters(params *wire.TransportParameters) err
}
// check the original_destination_connection_id
if !params.OriginalDestinationConnectionID.Equal(s.origDestConnID) {
return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID)
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID))
}
if s.retrySrcConnID != nil { // a Retry was performed
if params.RetrySourceConnectionID == nil {
return errors.New("missing retry_source_connection_id")
return qerr.NewError(qerr.TransportParameterError, "missing retry_source_connection_id")
}
if !(*params.RetrySourceConnectionID).Equal(*s.retrySrcConnID) {
return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID)
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID))
}
} else if params.RetrySourceConnectionID != nil {
return errors.New("received retry_source_connection_id, although no Retry was performed")
return qerr.NewError(qerr.TransportParameterError, "received retry_source_connection_id, although no Retry was performed")
}
return nil
}
@@ -1766,7 +1718,7 @@ func (s *session) sendPacket() (bool, error) {
s.sendQueue.Send(packet.buffer)
return true, nil
}
if !s.config.DisablePathMTUDiscovery && s.mtuDiscoverer.ShouldSendProbe(now) {
if !s.config.DisablePathMTUDiscovery && s.handshakeComplete && s.mtuDiscoverer.ShouldSendProbe(now) {
packet, err := s.packer.PackMTUProbePacket(s.mtuDiscoverer.GetPing())
if err != nil {
return false, err
@@ -1792,21 +1744,8 @@ func (s *session) sendPackedPacket(packet *packedPacket, now time.Time) {
s.sendQueue.Send(packet.buffer)
}
func (s *session) sendConnectionClose(e error) ([]byte, error) {
var packet *coalescedPacket
var err error
var transportErr *qerr.TransportError
var applicationErr *qerr.ApplicationError
if errors.As(e, &transportErr) {
packet, err = s.packer.PackConnectionClose(transportErr)
} else if errors.As(e, &applicationErr) {
packet, err = s.packer.PackApplicationClose(applicationErr)
} else {
packet, err = s.packer.PackConnectionClose(&qerr.TransportError{
ErrorCode: qerr.InternalError,
ErrorMessage: fmt.Sprintf("session BUG: unspecified error type (msg: %s)", e.Error()),
})
}
func (s *session) sendConnectionClose(quicErr *qerr.QuicError) ([]byte, error) {
packet, err := s.packer.PackConnectionClose(quicErr)
if err != nil {
return nil, err
}

View File

@@ -86,6 +86,16 @@ type stream struct {
var _ Stream = &stream{}
type streamCanceledError struct {
error
errorCode protocol.ApplicationErrorCode
}
func (streamCanceledError) Canceled() bool { return true }
func (e streamCanceledError) ErrorCode() protocol.ApplicationErrorCode { return e.errorCode }
var _ StreamError = &streamCanceledError{}
// newStream creates a new Stream
func newStream(streamID protocol.StreamID,
sender streamSender,

View File

@@ -209,10 +209,7 @@ func (m *streamsMap) DeleteStream(id protocol.StreamID) error {
func (m *streamsMap) GetOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) {
str, err := m.getOrOpenReceiveStream(id)
if err != nil {
return nil, &qerr.TransportError{
ErrorCode: qerr.StreamStateError,
ErrorMessage: err.Error(),
}
return nil, qerr.NewError(qerr.StreamStateError, err.Error())
}
return str, nil
}
@@ -243,10 +240,7 @@ func (m *streamsMap) getOrOpenReceiveStream(id protocol.StreamID) (receiveStream
func (m *streamsMap) GetOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) {
str, err := m.getOrOpenSendStream(id)
if err != nil {
return nil, &qerr.TransportError{
ErrorCode: qerr.StreamStateError,
ErrorMessage: err.Error(),
}
return nil, qerr.NewError(qerr.StreamStateError, err.Error())
}
return str, nil
}

View File

@@ -145,7 +145,7 @@ func (m *incomingBidiStreamsMap) DeleteStream(num protocol.StreamNum) error {
func (m *incomingBidiStreamsMap) deleteStream(num protocol.StreamNum) error {
if _, ok := m.streams[num]; !ok {
return streamError{
message: "tried to delete unknown incoming stream %d",
message: "Tried to delete unknown incoming stream %d",
nums: []protocol.StreamNum{num},
}
}
@@ -156,7 +156,7 @@ func (m *incomingBidiStreamsMap) deleteStream(num protocol.StreamNum) error {
entry, ok := m.streams[num]
if ok && entry.shouldDelete {
return streamError{
message: "tried to delete incoming stream %d multiple times",
message: "Tried to delete incoming stream %d multiple times",
nums: []protocol.StreamNum{num},
}
}

View File

@@ -143,7 +143,7 @@ func (m *incomingItemsMap) DeleteStream(num protocol.StreamNum) error {
func (m *incomingItemsMap) deleteStream(num protocol.StreamNum) error {
if _, ok := m.streams[num]; !ok {
return streamError{
message: "tried to delete unknown incoming stream %d",
message: "Tried to delete unknown incoming stream %d",
nums: []protocol.StreamNum{num},
}
}
@@ -154,7 +154,7 @@ func (m *incomingItemsMap) deleteStream(num protocol.StreamNum) error {
entry, ok := m.streams[num]
if ok && entry.shouldDelete {
return streamError{
message: "tried to delete incoming stream %d multiple times",
message: "Tried to delete incoming stream %d multiple times",
nums: []protocol.StreamNum{num},
}
}

View File

@@ -145,7 +145,7 @@ func (m *incomingUniStreamsMap) DeleteStream(num protocol.StreamNum) error {
func (m *incomingUniStreamsMap) deleteStream(num protocol.StreamNum) error {
if _, ok := m.streams[num]; !ok {
return streamError{
message: "tried to delete unknown incoming stream %d",
message: "Tried to delete unknown incoming stream %d",
nums: []protocol.StreamNum{num},
}
}
@@ -156,7 +156,7 @@ func (m *incomingUniStreamsMap) deleteStream(num protocol.StreamNum) error {
entry, ok := m.streams[num]
if ok && entry.shouldDelete {
return streamError{
message: "tried to delete incoming stream %d multiple times",
message: "Tried to delete incoming stream %d multiple times",
nums: []protocol.StreamNum{num},
}
}

View File

@@ -157,7 +157,7 @@ func (m *outgoingBidiStreamsMap) DeleteStream(num protocol.StreamNum) error {
if _, ok := m.streams[num]; !ok {
return streamError{
message: "tried to delete unknown outgoing stream %d",
message: "Tried to delete unknown outgoing stream %d",
nums: []protocol.StreamNum{num},
}
}

View File

@@ -155,7 +155,7 @@ func (m *outgoingItemsMap) DeleteStream(num protocol.StreamNum) error {
if _, ok := m.streams[num]; !ok {
return streamError{
message: "tried to delete unknown outgoing stream %d",
message: "Tried to delete unknown outgoing stream %d",
nums: []protocol.StreamNum{num},
}
}

View File

@@ -157,7 +157,7 @@ func (m *outgoingUniStreamsMap) DeleteStream(num protocol.StreamNum) error {
if _, ok := m.streams[num]; !ok {
return streamError{
message: "tried to delete unknown outgoing stream %d",
message: "Tried to delete unknown outgoing stream %d",
nums: []protocol.StreamNum{num},
}
}