Update quic-go

This commit is contained in:
Frank Denis 2022-11-19 23:01:21 +01:00
parent a89d96144f
commit 5f4dfc5d6e
18 changed files with 136 additions and 59 deletions

4
go.mod
View File

@ -18,10 +18,10 @@ require (
github.com/jedisct1/xsecretbox v0.0.0-20210927135450-ebe41aef7bef
github.com/k-sone/critbitgo v1.4.0
github.com/kardianos/service v1.2.2
github.com/lucas-clemente/quic-go v0.30.0
github.com/lucas-clemente/quic-go v0.31.0
github.com/miekg/dns v1.1.50
github.com/powerman/check v1.6.0
golang.org/x/crypto v0.2.0
golang.org/x/crypto v0.3.0
golang.org/x/net v0.2.0
golang.org/x/sys v0.2.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0

8
go.sum
View File

@ -432,8 +432,8 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lucas-clemente/quic-go v0.30.0 h1:nwLW0h8ahVQ5EPTIM7uhl/stHqQDea15oRlYKZmw2O0=
github.com/lucas-clemente/quic-go v0.30.0/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk=
github.com/lucas-clemente/quic-go v0.31.0 h1:MfNp3fk0wjWRajw6quMFA3ap1AVtlU+2mtwmbVogB2M=
github.com/lucas-clemente/quic-go v0.31.0/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@ -739,8 +739,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=

View File

@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"net"
"strings"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
@ -232,13 +231,10 @@ func newClient(
tlsConf = tlsConf.Clone()
}
if tlsConf.ServerName == "" {
sni := host
if strings.IndexByte(sni, ':') != -1 {
var err error
sni, _, err = net.SplitHostPort(sni)
if err != nil {
return nil, err
}
sni, _, err := net.SplitHostPort(host)
if err != nil {
// It's ok if net.SplitHostPort returns an error - it could be a hostname/IP address without a port.
sni = host
}
tlsConf.ServerName = sni

View File

@ -738,6 +738,7 @@ func (s *connection) ConnectionState() ConnectionState {
return ConnectionState{
TLS: s.cryptoStreamHandler.ConnectionState(),
SupportsDatagrams: s.supportsDatagrams(),
Version: s.version,
}
}
@ -1217,7 +1218,7 @@ func (s *connection) handleUnpackedPacket(
// We create the connection as soon as we receive the first packet from the client.
// We do that before authenticating the packet.
// That means that if the source connection ID was corrupted,
// we might have create a connection with an incorrect source connection ID.
// we might have created a connection with an incorrect source connection ID.
// Once we authenticate the first packet, we need to update it.
if s.perspective == protocol.PerspectiveServer {
if packet.hdr.SrcConnectionID != s.handshakeDestConnID {

View File

@ -0,0 +1,55 @@
package http3
import (
"io"
"github.com/lucas-clemente/quic-go/quicvarint"
)
// CapsuleType is the type of the capsule.
type CapsuleType uint64
type exactReader struct {
R *io.LimitedReader
}
func (r *exactReader) Read(b []byte) (int, error) {
n, err := r.R.Read(b)
if r.R.N > 0 {
return n, io.ErrUnexpectedEOF
}
return n, err
}
// ParseCapsule parses the header of a Capsule.
// It returns an io.LimitedReader that can be used to read the Capsule value.
// The Capsule value must be read entirely (i.e. until the io.EOF) before using r again.
func ParseCapsule(r quicvarint.Reader) (CapsuleType, io.Reader, error) {
ct, err := quicvarint.Read(r)
if err != nil {
if err == io.EOF {
return 0, nil, io.ErrUnexpectedEOF
}
return 0, nil, err
}
l, err := quicvarint.Read(r)
if err != nil {
if err == io.EOF {
return 0, nil, io.ErrUnexpectedEOF
}
return 0, nil, err
}
return CapsuleType(ct), &exactReader{R: io.LimitReader(r, int64(l)).(*io.LimitedReader)}, nil
}
// WriteCapsule writes a capsule
func WriteCapsule(w quicvarint.Writer, ct CapsuleType, value []byte) error {
b := make([]byte, 0, 16)
b = quicvarint.Append(b, uint64(ct))
b = quicvarint.Append(b, uint64(len(value)))
if _, err := w.Write(b); err != nil {
return err
}
_, err := w.Write(value)
return err
}

View File

@ -390,6 +390,7 @@ func (c *client) doRequest(req *http.Request, str quic.Stream, opt RoundTripOpt,
ProtoMajor: 3,
Header: http.Header{},
TLS: &connState,
Request: req,
}
for _, hf := range hfs {
switch hf.Name {

View File

@ -201,11 +201,20 @@ type EarlyConnection interface {
NextConnection() Connection
}
// StatelessResetKey is a key used to derive stateless reset tokens.
type StatelessResetKey [32]byte
// A ConnectionID is a QUIC Connection ID, as defined in RFC 9000.
// It is not able to handle QUIC Connection IDs longer than 20 bytes,
// as they are allowed by RFC 8999.
type ConnectionID = protocol.ConnectionID
// ConnectionIDFromBytes interprets b as a Connection ID. It panics if b is
// longer than 20 bytes.
func ConnectionIDFromBytes(b []byte) ConnectionID {
return protocol.ParseConnectionID(b)
}
// A ConnectionIDGenerator is an interface that allows clients to implement their own format
// for the Connection IDs that servers/clients use as SrcConnectionID in QUIC packets.
//
@ -303,7 +312,7 @@ type Config struct {
MaxIncomingUniStreams int64
// The StatelessResetKey is used to generate stateless reset tokens.
// If no key is configured, sending of stateless resets is disabled.
StatelessResetKey []byte
StatelessResetKey *StatelessResetKey
// KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive.
// If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most
// every half of MaxIdleTimeout, whichever is smaller).
@ -325,6 +334,7 @@ type Config struct {
type ConnectionState struct {
TLS handshake.ConnectionState
SupportsDatagrams bool
Version VersionNumber
}
// A Listener for incoming QUIC connections

View File

@ -23,6 +23,8 @@ const (
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
// The PTO duration uses exponential backoff, but is truncated to a maximum value, as allowed by RFC 8961, section 4.4.
maxPTODuration = 60 * time.Second
)
type packetNumberSpace struct {
@ -457,6 +459,14 @@ func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.Encryptio
return lossTime, encLevel
}
func (h *sentPacketHandler) getScaledPTO(includeMaxAckDelay bool) time.Duration {
pto := h.rttStats.PTO(includeMaxAckDelay) << h.ptoCount
if pto > maxPTODuration || pto <= 0 {
return maxPTODuration
}
return pto
}
// 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,
@ -465,7 +475,7 @@ func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protoc
if h.peerCompletedAddressValidation {
return
}
t := time.Now().Add(h.rttStats.PTO(false) << h.ptoCount)
t := time.Now().Add(h.getScaledPTO(false))
if h.initialPackets != nil {
return t, protocol.EncryptionInitial, true
}
@ -475,18 +485,18 @@ func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protoc
if h.initialPackets != nil {
encLevel = protocol.EncryptionInitial
if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() {
pto = t.Add(h.rttStats.PTO(false) << h.ptoCount)
pto = t.Add(h.getScaledPTO(false))
}
}
if h.handshakePackets != nil && !h.handshakePackets.lastAckElicitingPacketTime.IsZero() {
t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.rttStats.PTO(false) << h.ptoCount)
t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(false))
if pto.IsZero() || (!t.IsZero() && t.Before(pto)) {
pto = t
encLevel = protocol.EncryptionHandshake
}
}
if h.handshakeConfirmed && !h.appDataPackets.lastAckElicitingPacketTime.IsZero() {
t := h.appDataPackets.lastAckElicitingPacketTime.Add(h.rttStats.PTO(true) << h.ptoCount)
t := h.appDataPackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(true))
if pto.IsZero() || (!t.IsZero() && t.Before(pto)) {
pto = t
encLevel = protocol.Encryption1RTT

View File

@ -47,7 +47,7 @@ func (c *baseFlowController) AddBytesSent(n protocol.ByteCount) {
c.bytesSent += n
}
// UpdateSendWindow is be called after receiving a MAX_{STREAM_}DATA frame.
// UpdateSendWindow is called after receiving a MAX_{STREAM_}DATA frame.
func (c *baseFlowController) UpdateSendWindow(offset protocol.ByteCount) {
if offset > c.sendWindow {
c.sendWindow = offset

View File

@ -1,4 +1,4 @@
package logging
//go:generate sh -c "mockgen -package logging -self_package github.com/lucas-clemente/quic-go/logging -destination mock_connection_tracer_test.go github.com/lucas-clemente/quic-go/logging ConnectionTracer"
//go:generate sh -c "mockgen -package logging -self_package github.com/lucas-clemente/quic-go/logging -destination mock_tracer_test.go github.com/lucas-clemente/quic-go/logging Tracer"
//go:generate sh -c "go run github.com/golang/mock/mockgen -package logging -self_package github.com/lucas-clemente/quic-go/logging -destination mock_connection_tracer_test.go github.com/lucas-clemente/quic-go/logging ConnectionTracer"
//go:generate sh -c "go run github.com/golang/mock/mockgen -package logging -self_package github.com/lucas-clemente/quic-go/logging -destination mock_tracer_test.go github.com/lucas-clemente/quic-go/logging Tracer"

View File

@ -23,5 +23,5 @@ package quic
//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager"
//go:generate sh -c "./mockgen_private.sh quic mock_multiplexer_test.go github.com/lucas-clemente/quic-go multiplexer"
//go:generate sh -c "./mockgen_private.sh quic mock_batch_conn_test.go github.com/lucas-clemente/quic-go batchConn"
//go:generate sh -c "mockgen -package quic -self_package github.com/lucas-clemente/quic-go -destination mock_token_store_test.go github.com/lucas-clemente/quic-go TokenStore"
//go:generate sh -c "mockgen -package quic -self_package github.com/lucas-clemente/quic-go -destination mock_packetconn_test.go net PacketConn"
//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/lucas-clemente/quic-go -destination mock_token_store_test.go github.com/lucas-clemente/quic-go TokenStore"
//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/lucas-clemente/quic-go -destination mock_packetconn_test.go net PacketConn"

View File

@ -44,6 +44,6 @@ AUX_FILES=$(IFS=, ; echo "${AUX[*]}")
## create a public alias for the interface, so that mockgen can process it
echo -e "package $1\n" > $TMPFILE
echo "$INTERFACE" | sed "s/$ORIG_INTERFACE_NAME/$INTERFACE_NAME/" >> $TMPFILE
mockgen -package $1 -self_package $3 -destination $DEST -source=$TMPFILE -aux_files $AUX_FILES
go run github.com/golang/mock/mockgen -package $1 -self_package $3 -destination $DEST -source=$TMPFILE -aux_files $AUX_FILES
sed "s/$TMPFILE/$SRC/" "$DEST" > "$DEST.new" && mv "$DEST.new" "$DEST"
rm "$TMPFILE"

View File

@ -1,7 +1,6 @@
package quic
import (
"bytes"
"fmt"
"net"
"sync"
@ -20,13 +19,13 @@ type indexableConn interface {
}
type multiplexer interface {
AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte, tracer logging.Tracer) (packetHandlerManager, error)
AddConn(c net.PacketConn, connIDLen int, statelessResetKey *StatelessResetKey, tracer logging.Tracer) (packetHandlerManager, error)
RemoveConn(indexableConn) error
}
type connManager struct {
connIDLen int
statelessResetKey []byte
statelessResetKey *StatelessResetKey
tracer logging.Tracer
manager packetHandlerManager
}
@ -37,7 +36,7 @@ type connMultiplexer struct {
mutex sync.Mutex
conns map[string] /* LocalAddr().String() */ connManager
newPacketHandlerManager func(net.PacketConn, int, []byte, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests
newPacketHandlerManager func(net.PacketConn, int, *StatelessResetKey, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests
logger utils.Logger
}
@ -58,7 +57,7 @@ func getMultiplexer() multiplexer {
func (m *connMultiplexer) AddConn(
c net.PacketConn,
connIDLen int,
statelessResetKey []byte,
statelessResetKey *StatelessResetKey,
tracer logging.Tracer,
) (packetHandlerManager, error) {
m.mutex.Lock()
@ -83,7 +82,7 @@ func (m *connMultiplexer) AddConn(
if p.connIDLen != connIDLen {
return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen)
}
if statelessResetKey != nil && !bytes.Equal(p.statelessResetKey, statelessResetKey) {
if statelessResetKey != nil && p.statelessResetKey != statelessResetKey {
return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn")
}
if tracer != p.tracer {

View File

@ -105,7 +105,7 @@ var receiveBufferWarningOnce sync.Once
func newPacketHandlerMap(
c net.PacketConn,
connIDLen int,
statelessResetKey []byte,
statelessResetKey *StatelessResetKey,
tracer logging.Tracer,
logger utils.Logger,
) (packetHandlerManager, error) {
@ -132,11 +132,13 @@ func newPacketHandlerMap(
deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout,
zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration,
closeQueue: make(chan closePacket, 4),
statelessResetEnabled: len(statelessResetKey) > 0,
statelessResetHasher: hmac.New(sha256.New, statelessResetKey),
statelessResetEnabled: statelessResetKey != nil,
tracer: tracer,
logger: logger,
}
if m.statelessResetEnabled {
m.statelessResetHasher = hmac.New(sha256.New, statelessResetKey[:])
}
go m.listen()
go m.runCloseQueue()

View File

@ -122,7 +122,7 @@ func (s *sendStream) Write(p []byte) (int, error) {
var copied bool
var deadline time.Time
// As soon as dataForWriting becomes smaller than a certain size x, we copy all the data to a STREAM frame (s.nextFrame),
// which can the be popped the next time we assemble a packet.
// which can then be popped the next time we assemble a packet.
// This allows us to return Write() when all data but x bytes have been sent out.
// When the user now calls Close(), this is much more likely to happen before we popped that last STREAM frame,
// allowing us to set the FIN bit on that frame (instead of sending an empty STREAM frame with FIN).

View File

@ -161,18 +161,20 @@ func (c *oobConn) ReadPacket() (*receivedPacket, error) {
msg := c.messages[c.readPos]
buffer := c.buffers[c.readPos]
c.readPos++
ctrlMsgs, err := unix.ParseSocketControlMessage(msg.OOB[:msg.NN])
if err != nil {
return nil, err
}
data := msg.OOB[:msg.NN]
var ecn protocol.ECN
var destIP net.IP
var ifIndex uint32
for _, ctrlMsg := range ctrlMsgs {
if ctrlMsg.Header.Level == unix.IPPROTO_IP {
switch ctrlMsg.Header.Type {
for len(data) > 0 {
hdr, body, remainder, err := unix.ParseOneSocketControlMessage(data)
if err != nil {
return nil, err
}
if hdr.Level == unix.IPPROTO_IP {
switch hdr.Type {
case msgTypeIPTOS:
ecn = protocol.ECN(ctrlMsg.Data[0] & ecnMask)
ecn = protocol.ECN(body[0] & ecnMask)
case msgTypeIPv4PKTINFO:
// struct in_pktinfo {
// unsigned int ipi_ifindex; /* Interface index */
@ -181,33 +183,34 @@ func (c *oobConn) ReadPacket() (*receivedPacket, error) {
// address */
// };
ip := make([]byte, 4)
if len(ctrlMsg.Data) == 12 {
ifIndex = binary.LittleEndian.Uint32(ctrlMsg.Data)
copy(ip, ctrlMsg.Data[8:12])
} else if len(ctrlMsg.Data) == 4 {
if len(body) == 12 {
ifIndex = binary.LittleEndian.Uint32(body)
copy(ip, body[8:12])
} else if len(body) == 4 {
// FreeBSD
copy(ip, ctrlMsg.Data)
copy(ip, body)
}
destIP = net.IP(ip)
}
}
if ctrlMsg.Header.Level == unix.IPPROTO_IPV6 {
switch ctrlMsg.Header.Type {
if hdr.Level == unix.IPPROTO_IPV6 {
switch hdr.Type {
case unix.IPV6_TCLASS:
ecn = protocol.ECN(ctrlMsg.Data[0] & ecnMask)
ecn = protocol.ECN(body[0] & ecnMask)
case msgTypeIPv6PKTINFO:
// struct in6_pktinfo {
// struct in6_addr ipi6_addr; /* src/dst IPv6 address */
// unsigned int ipi6_ifindex; /* send/recv interface index */
// };
if len(ctrlMsg.Data) == 20 {
if len(body) == 20 {
ip := make([]byte, 16)
copy(ip, ctrlMsg.Data[:16])
copy(ip, body[:16])
destIP = net.IP(ip)
ifIndex = binary.LittleEndian.Uint32(ctrlMsg.Data[16:])
ifIndex = binary.LittleEndian.Uint32(body[16:])
}
}
}
data = remainder
}
var info *packetInfo
if destIP != nil {

View File

@ -532,7 +532,7 @@ func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
return false
}
paddingBits := uint8(bytes[0])
paddingBits := bytes[0]
bytes = bytes[1:]
if paddingBits > 7 ||
len(bytes) == 0 && paddingBits != 0 ||
@ -554,7 +554,7 @@ func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool {
return false
}
paddingBits := uint8(bytes[0])
paddingBits := bytes[0]
if paddingBits != 0 {
return false
}

4
vendor/modules.txt vendored
View File

@ -316,7 +316,7 @@ github.com/ldez/gomoddirectives
# github.com/ldez/tagliatelle v0.2.0
## explicit; go 1.16
github.com/ldez/tagliatelle
# github.com/lucas-clemente/quic-go v0.30.0
# github.com/lucas-clemente/quic-go v0.31.0
## explicit; go 1.18
github.com/lucas-clemente/quic-go
github.com/lucas-clemente/quic-go/http3
@ -578,7 +578,7 @@ github.com/uudashr/gocognit
# github.com/yeya24/promlinter v0.1.0
## explicit; go 1.14
github.com/yeya24/promlinter
# golang.org/x/crypto v0.2.0
# golang.org/x/crypto v0.3.0
## explicit; go 1.17
golang.org/x/crypto/blake2b
golang.org/x/crypto/chacha20