Commit 917534e3 authored by Marten Seemann's avatar Marten Seemann Committed by Matt Holt

vendor: update quic-go to v0.10.1 (#2431)

parent 8ab447e6
......@@ -25,12 +25,9 @@ type client struct {
// If it is started with Dial, we take a packet conn as a parameter.
createdPacketConn bool
hostname string
packetHandlers packetHandlerManager
token []byte
numRetries int
token []byte
versionNegotiated bool // has the server accepted our version
receivedVersionNegotiationPacket bool
......@@ -159,13 +156,12 @@ func newClient(
closeCallback func(protocol.ConnectionID),
createdPacketConn bool,
) (*client, error) {
var hostname string
if tlsConf != nil {
hostname = tlsConf.ServerName
if tlsConf == nil {
tlsConf = &tls.Config{}
}
if hostname == "" {
if tlsConf.ServerName == "" {
var err error
hostname, _, err = net.SplitHostPort(host)
tlsConf.ServerName, _, err = net.SplitHostPort(host)
if err != nil {
return nil, err
}
......@@ -186,7 +182,6 @@ func newClient(
c := &client{
conn: &conn{pconn: pconn, currentAddr: remoteAddr},
createdPacketConn: createdPacketConn,
hostname: hostname,
tlsConf: tlsConf,
config: config,
version: config.Versions[0],
......@@ -286,7 +281,7 @@ func (c *client) generateConnectionIDs() error {
}
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.hostname, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
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)
var err error
if c.version.UsesTLS() {
......@@ -324,7 +319,6 @@ func (c *client) dialTLS(ctx context.Context) error {
return err
}
mintConf.ExtensionHandler = extHandler
mintConf.ServerName = c.hostname
c.mintConf = mintConf
if err := c.createNewTLSSession(extHandler.GetPeerParams(), c.version); err != nil {
......@@ -483,19 +477,18 @@ func (c *client) handleVersionNegotiationPacket(hdr *wire.Header) error {
func (c *client) handleRetryPacket(hdr *wire.Header) {
c.logger.Debugf("<- Received Retry")
hdr.Log(c.logger)
// A server that performs multiple retries must use a source connection ID of at least 8 bytes.
// Only a server that won't send additional Retries can use shorter connection IDs.
if hdr.OrigDestConnectionID.Len() < protocol.MinConnectionIDLenInitial {
c.logger.Debugf("Received a Retry with a too short Original Destination Connection ID: %d bytes, must have at least %d bytes.", hdr.OrigDestConnectionID.Len(), protocol.MinConnectionIDLenInitial)
if !hdr.OrigDestConnectionID.Equal(c.destConnID) {
c.logger.Debugf("Ignoring spoofed Retry. Original Destination Connection ID: %s, expected: %s", hdr.OrigDestConnectionID, c.destConnID)
return
}
if !hdr.OrigDestConnectionID.Equal(c.destConnID) {
c.logger.Debugf("Received spoofed Retry. Original Destination Connection ID: %s, expected: %s", hdr.OrigDestConnectionID, c.destConnID)
if hdr.SrcConnectionID.Equal(c.destConnID) {
c.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.")
return
}
c.numRetries++
if c.numRetries > protocol.MaxRetries {
c.session.destroy(qerr.CryptoTooManyRejects)
// If a token is already set, this means that we already received a Retry from the server.
// Ignore this Retry packet.
if len(c.token) > 0 {
c.logger.Debugf("Ignoring Retry, since a Retry was already received.")
return
}
c.destConnID = hdr.SrcConnectionID
......@@ -513,7 +506,6 @@ func (c *client) createNewGQUICSession() error {
sess, err := newClientSession(
c.conn,
runner,
c.hostname,
c.version,
c.destConnID,
c.srcConnID,
......
......@@ -13,6 +13,7 @@ type cryptoStream interface {
io.Reader
io.Writer
handleStreamFrame(*wire.StreamFrame) error
hasData() bool
popStreamFrame(protocol.ByteCount) (*wire.StreamFrame, bool)
closeForShutdown(error)
setReadOffset(protocol.ByteCount)
......
......@@ -16,6 +16,7 @@ import (
func main() {
verbose := flag.Bool("v", false, "verbose")
tls := flag.Bool("tls", false, "activate support for IETF QUIC (work in progress)")
quiet := flag.Bool("q", false, "don't print the data")
flag.Parse()
urls := flag.Args()
......@@ -57,8 +58,12 @@ func main() {
if err != nil {
panic(err)
}
logger.Infof("Request Body:")
logger.Infof("%s", body.Bytes())
if *quiet {
logger.Infof("Request Body: %d bytes", body.Len())
} else {
logger.Infof("Request Body:")
logger.Infof("%s", body.Bytes())
}
wg.Done()
}(addr)
}
......
......@@ -7,23 +7,25 @@ import (
"github.com/lucas-clemente/quic-go/internal/wire"
)
type streamFramer struct {
type framer struct {
streamGetter streamGetter
cryptoStream cryptoStream
version protocol.VersionNumber
streamQueueMutex sync.Mutex
activeStreams map[protocol.StreamID]struct{}
streamQueue []protocol.StreamID
hasCryptoStreamData bool
streamQueueMutex sync.Mutex
activeStreams map[protocol.StreamID]struct{}
streamQueue []protocol.StreamID
controlFrameMutex sync.Mutex
controlFrames []wire.Frame
}
func newStreamFramer(
func newFramer(
cryptoStream cryptoStream,
streamGetter streamGetter,
v protocol.VersionNumber,
) *streamFramer {
return &streamFramer{
) *framer {
return &framer{
streamGetter: streamGetter,
cryptoStream: cryptoStream,
activeStreams: make(map[protocol.StreamID]struct{}),
......@@ -31,13 +33,32 @@ func newStreamFramer(
}
}
func (f *streamFramer) AddActiveStream(id protocol.StreamID) {
if id == f.version.CryptoStreamID() { // the crypto stream is handled separately
f.streamQueueMutex.Lock()
f.hasCryptoStreamData = true
f.streamQueueMutex.Unlock()
return
func (f *framer) QueueControlFrame(frame wire.Frame) {
f.controlFrameMutex.Lock()
f.controlFrames = append(f.controlFrames, frame)
f.controlFrameMutex.Unlock()
}
func (f *framer) AppendControlFrames(frames []wire.Frame, maxLen protocol.ByteCount) ([]wire.Frame, protocol.ByteCount) {
var length protocol.ByteCount
f.controlFrameMutex.Lock()
for len(f.controlFrames) > 0 {
frame := f.controlFrames[len(f.controlFrames)-1]
frameLen := frame.Length(f.version)
if length+frameLen > maxLen {
break
}
frames = append(frames, frame)
length += frameLen
f.controlFrames = f.controlFrames[:len(f.controlFrames)-1]
}
f.controlFrameMutex.Unlock()
return frames, length
}
// AddActiveStream adds a stream that has data to write.
// It should not be used for the crypto stream.
func (f *framer) AddActiveStream(id protocol.StreamID) {
f.streamQueueMutex.Lock()
if _, ok := f.activeStreams[id]; !ok {
f.streamQueue = append(f.streamQueue, id)
......@@ -46,29 +67,13 @@ func (f *streamFramer) AddActiveStream(id protocol.StreamID) {
f.streamQueueMutex.Unlock()
}
func (f *streamFramer) HasCryptoStreamData() bool {
f.streamQueueMutex.Lock()
hasCryptoStreamData := f.hasCryptoStreamData
f.streamQueueMutex.Unlock()
return hasCryptoStreamData
}
func (f *streamFramer) PopCryptoStreamFrame(maxLen protocol.ByteCount) *wire.StreamFrame {
f.streamQueueMutex.Lock()
frame, hasMoreData := f.cryptoStream.popStreamFrame(maxLen)
f.hasCryptoStreamData = hasMoreData
f.streamQueueMutex.Unlock()
return frame
}
func (f *streamFramer) PopStreamFrames(maxTotalLen protocol.ByteCount) []*wire.StreamFrame {
var currentLen protocol.ByteCount
var frames []*wire.StreamFrame
func (f *framer) AppendStreamFrames(frames []wire.Frame, maxLen protocol.ByteCount) []wire.Frame {
var length protocol.ByteCount
f.streamQueueMutex.Lock()
// pop STREAM frames, until less than MinStreamFrameSize bytes are left in the packet
numActiveStreams := len(f.streamQueue)
for i := 0; i < numActiveStreams; i++ {
if maxTotalLen-currentLen < protocol.MinStreamFrameSize {
if maxLen-length < protocol.MinStreamFrameSize {
break
}
id := f.streamQueue[0]
......@@ -81,7 +86,7 @@ func (f *streamFramer) PopStreamFrames(maxTotalLen protocol.ByteCount) []*wire.S
delete(f.activeStreams, id)
continue
}
frame, hasMoreData := str.popStreamFrame(maxTotalLen - currentLen)
frame, hasMoreData := str.popStreamFrame(maxLen - length)
if hasMoreData { // put the stream back in the queue (at the end)
f.streamQueue = append(f.streamQueue, id)
} else { // no more data to send. Stream is not active any more
......@@ -91,7 +96,7 @@ func (f *streamFramer) PopStreamFrames(maxTotalLen protocol.ByteCount) []*wire.S
continue
}
frames = append(frames, frame)
currentLen += frame.Length(f.version)
length += frame.Length(f.version)
}
f.streamQueueMutex.Unlock()
return frames
......
......@@ -141,7 +141,7 @@ func (s *Server) handleHeaderStream(session streamCreator) {
// In this case, the session has already logged the error, so we don't
// need to log it again.
errorCode := qerr.InternalError
if qerr, ok := err.(*qerr.QuicError); !ok {
if qerr, ok := err.(*qerr.QuicError); ok {
errorCode = qerr.ErrorCode
s.logger.Errorf("error handling h2 request: %s", err.Error())
}
......
......@@ -23,8 +23,6 @@ const (
VersionGQUIC43 = protocol.Version43
// VersionGQUIC44 is gQUIC version 44.
VersionGQUIC44 = protocol.Version44
// VersionMilestone0_10_0 uses TLS
VersionMilestone0_10_0 = protocol.VersionMilestone0_10_0
)
// A Cookie can be used to verify the ownership of the client address.
......
......@@ -148,6 +148,7 @@ func (h *sentPacketHandler) SentPacketsAsRetransmission(packets []*Packet, retra
func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* isRetransmittable */ {
for p := h.lastSentPacketNumber + 1; p < packet.PacketNumber; p++ {
h.logger.Debugf("Skipping packet number %#x", p)
h.skippedPackets = append(h.skippedPackets, p)
if len(h.skippedPackets) > protocol.MaxTrackedSkippedPackets {
h.skippedPackets = h.skippedPackets[1:]
......@@ -633,7 +634,7 @@ func (h *sentPacketHandler) computeRTOTimeout() time.Duration {
}
rto = utils.MaxDuration(rto, minRTOTimeout)
// Exponential backoff
rto = rto << h.rtoCount
rto <<= h.rtoCount
return utils.MinDuration(rto, maxRTOTimeout)
}
......
......@@ -193,7 +193,7 @@ func (c *cubicSender) OnPacketLost(
if c.congestionWindow >= 2*c.initialCongestionWindow {
c.minSlowStartExitWindow = c.congestionWindow / 2
}
c.congestionWindow = c.congestionWindow - protocol.DefaultTCPMSS
c.congestionWindow -= protocol.DefaultTCPMSS
} else if c.reno {
c.congestionWindow = protocol.ByteCount(float32(c.congestionWindow) * c.RenoBeta())
} else {
......
......@@ -56,8 +56,7 @@ func (c *certChain) GetLeafCert(sni string) ([]byte, error) {
}
func (c *certChain) getCertForSNI(sni string) (*tls.Certificate, error) {
conf := c.config
conf, err := maybeGetConfigForClient(conf, sni)
conf, err := maybeGetConfigForClient(c.config, sni)
if err != nil {
return nil, err
}
......@@ -107,7 +106,13 @@ func maybeGetConfigForClient(c *tls.Config, sni string) (*tls.Config, error) {
if c.GetConfigForClient == nil {
return c, nil
}
return c.GetConfigForClient(&tls.ClientHelloInfo{
ServerName: sni,
})
confForClient, err := c.GetConfigForClient(&tls.ClientHelloInfo{ServerName: sni})
if err != nil {
return nil, err
}
// if GetConfigForClient returns nil, use the original config
if confForClient == nil {
return c, nil
}
return confForClient, nil
}
package crypto
import (
"crypto"
"crypto/hmac"
"encoding/binary"
)
// copied from https://github.com/cloudflare/tls-tris/blob/master/hkdf.go
func hkdfExtract(hash crypto.Hash, secret, salt []byte) []byte {
if salt == nil {
salt = make([]byte, hash.Size())
}
if secret == nil {
secret = make([]byte, hash.Size())
}
extractor := hmac.New(hash.New, salt)
extractor.Write(secret)
return extractor.Sum(nil)
}
// copied from https://github.com/cloudflare/tls-tris/blob/master/hkdf.go
func hkdfExpand(hash crypto.Hash, prk, info []byte, l int) []byte {
var (
expander = hmac.New(hash.New, prk)
res = make([]byte, l)
counter = byte(1)
prev []byte
)
if l > 255*expander.Size() {
panic("hkdf: requested too much output")
}
p := res
for len(p) > 0 {
expander.Reset()
expander.Write(prev)
expander.Write(info)
expander.Write([]byte{counter})
prev = expander.Sum(prev[:0])
counter++
n := copy(p, prev)
p = p[n:]
}
return res
}
func qhkdfExpand(secret []byte, label string, length int) []byte {
qlabel := make([]byte, 2+1+5+len(label))
binary.BigEndian.PutUint16(qlabel[0:2], uint16(length))
qlabel[2] = uint8(5 + len(label))
copy(qlabel[3:], []byte("QUIC "+label))
return hkdfExpand(crypto.SHA256, secret, qlabel, length)
}
package crypto
import (
"crypto"
"encoding/binary"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
......@@ -19,14 +16,6 @@ type TLSExporter interface {
ComputeExporter(label string, context []byte, keyLength int) ([]byte, error)
}
func qhkdfExpand(secret []byte, label string, length int) []byte {
qlabel := make([]byte, 2+1+5+len(label))
binary.BigEndian.PutUint16(qlabel[0:2], uint16(length))
qlabel[2] = uint8(5 + len(label))
copy(qlabel[3:], []byte("QUIC "+label))
return mint.HkdfExpand(crypto.SHA256, secret, qlabel, length)
}
// DeriveAESKeys derives the AES keys and creates a matching AES-GCM AEAD instance
func DeriveAESKeys(tls TLSExporter, pers protocol.Perspective) (AEAD, error) {
var myLabel, otherLabel string
......
......@@ -3,7 +3,6 @@ package crypto
import (
"crypto"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
......@@ -28,7 +27,7 @@ func newNullAEADAESGCM(connectionID protocol.ConnectionID, pers protocol.Perspec
}
func computeSecrets(connID protocol.ConnectionID) (clientSecret, serverSecret []byte) {
handshakeSecret := mint.HkdfExtract(crypto.SHA256, quicVersion1Salt, connID)
handshakeSecret := hkdfExtract(crypto.SHA256, connID, quicVersion1Salt)
clientSecret = qhkdfExpand(handshakeSecret, "client hs", crypto.SHA256.Size())
serverSecret = qhkdfExpand(handshakeSecret, "server hs", crypto.SHA256.Size())
return
......
......@@ -69,10 +69,9 @@ var (
// NewCryptoSetupClient creates a new CryptoSetup instance for a client
func NewCryptoSetupClient(
cryptoStream io.ReadWriter,
hostname string,
connID protocol.ConnectionID,
version protocol.VersionNumber,
tlsConfig *tls.Config,
tlsConf *tls.Config,
params *TransportParameters,
paramsChan chan<- TransportParameters,
handshakeEvent chan<- struct{},
......@@ -87,10 +86,10 @@ func NewCryptoSetupClient(
divNonceChan := make(chan struct{})
cs := &cryptoSetupClient{
cryptoStream: cryptoStream,
hostname: hostname,
hostname: tlsConf.ServerName,
connID: connID,
version: version,
certManager: crypto.NewCertManager(tlsConfig),
certManager: crypto.NewCertManager(tlsConf),
params: params,
keyDerivation: crypto.DeriveQuicCryptoAESKeys,
nullAEAD: nullAEAD,
......
......@@ -3,8 +3,8 @@ package handshake
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"sort"
"time"
"github.com/lucas-clemente/quic-go/internal/protocol"
......@@ -97,16 +97,18 @@ func (p *TransportParameters) getHelloMap() map[Tag][]byte {
}
func (p *TransportParameters) unmarshal(data []byte) error {
var foundIdleTimeout bool
// needed to check that every parameter is only sent at most once
var parameterIDs []transportParameterID
for len(data) >= 4 {
paramID := binary.BigEndian.Uint16(data[:2])
paramID := transportParameterID(binary.BigEndian.Uint16(data[:2]))
paramLen := int(binary.BigEndian.Uint16(data[2:4]))
data = data[4:]
if len(data) < paramLen {
return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", len(data), paramLen)
}
switch transportParameterID(paramID) {
parameterIDs = append(parameterIDs, paramID)
switch paramID {
case initialMaxStreamDataParameterID:
if paramLen != 4 {
return fmt.Errorf("wrong length for initial_max_stream_data: %d (expected 4)", paramLen)
......@@ -128,7 +130,6 @@ func (p *TransportParameters) unmarshal(data []byte) error {
}
p.MaxUniStreams = binary.BigEndian.Uint16(data[:2])
case idleTimeoutParameterID:
foundIdleTimeout = true
if paramLen != 2 {
return fmt.Errorf("wrong length for idle_timeout: %d (expected 2)", paramLen)
}
......@@ -156,12 +157,17 @@ func (p *TransportParameters) unmarshal(data []byte) error {
data = data[paramLen:]
}
// check that every transport parameter was sent at most once
sort.Slice(parameterIDs, func(i, j int) bool { return parameterIDs[i] < parameterIDs[j] })
for i := 0; i < len(parameterIDs)-1; i++ {
if parameterIDs[i] == parameterIDs[i+1] {
return fmt.Errorf("received duplicate transport parameter %#x", parameterIDs[i])
}
}
if len(data) != 0 {
return fmt.Errorf("should have read all data. Still have %d bytes", len(data))
}
if !foundIdleTimeout {
return errors.New("missing parameter")
}
return nil
}
......
package mocks
//go:generate sh -c "../mockgen_internal.sh mocks tls_extension_handler.go github.com/lucas-clemente/quic-go/internal/handshake TLSExtensionHandler"
//go:generate sh -c "../mockgen_internal.sh mocks sealer.go github.com/lucas-clemente/quic-go/internal/handshake Sealer"
//go:generate sh -c "../mockgen_internal.sh mocks stream_flow_controller.go github.com/lucas-clemente/quic-go/internal/flowcontrol StreamFlowController"
//go:generate sh -c "../mockgen_internal.sh mockackhandler ackhandler/sent_packet_handler.go github.com/lucas-clemente/quic-go/internal/ackhandler SentPacketHandler"
//go:generate sh -c "../mockgen_internal.sh mockackhandler ackhandler/received_packet_handler.go github.com/lucas-clemente/quic-go/internal/ackhandler ReceivedPacketHandler"
......
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/lucas-clemente/quic-go/internal/handshake (interfaces: Sealer)
// Package mocks is a generated GoMock package.
package mocks
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
protocol "github.com/lucas-clemente/quic-go/internal/protocol"
)
// MockSealer is a mock of Sealer interface
type MockSealer struct {
ctrl *gomock.Controller
recorder *MockSealerMockRecorder
}
// MockSealerMockRecorder is the mock recorder for MockSealer
type MockSealerMockRecorder struct {
mock *MockSealer
}
// NewMockSealer creates a new mock instance
func NewMockSealer(ctrl *gomock.Controller) *MockSealer {
mock := &MockSealer{ctrl: ctrl}
mock.recorder = &MockSealerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockSealer) EXPECT() *MockSealerMockRecorder {
return m.recorder
}
// Overhead mocks base method
func (m *MockSealer) Overhead() int {
ret := m.ctrl.Call(m, "Overhead")
ret0, _ := ret[0].(int)
return ret0
}
// Overhead indicates an expected call of Overhead
func (mr *MockSealerMockRecorder) Overhead() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Overhead", reflect.TypeOf((*MockSealer)(nil).Overhead))
}
// Seal mocks base method
func (m *MockSealer) Seal(arg0, arg1 []byte, arg2 protocol.PacketNumber, arg3 []byte) []byte {
ret := m.ctrl.Call(m, "Seal", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].([]byte)
return ret0
}
// Seal indicates an expected call of Seal
func (mr *MockSealerMockRecorder) Seal(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Seal", reflect.TypeOf((*MockSealer)(nil).Seal), arg0, arg1, arg2, arg3)
}
......@@ -149,6 +149,3 @@ const MinPacingDelay time.Duration = 100 * time.Microsecond
// DefaultConnectionIDLength is the connection ID length that is used for multiplexed connections
// if no other value is configured.
const DefaultConnectionIDLength = 4
// MaxRetries is the maximum number of Retries a client will do before failing the connection.
const MaxRetries = 3
......@@ -24,8 +24,6 @@ const (
VersionTLS VersionNumber = 101
VersionWhatever VersionNumber = 0 // for when the version doesn't matter
VersionUnknown VersionNumber = math.MaxUint32
VersionMilestone0_10_0 VersionNumber = 0x51474f02
)
// SupportedVersions lists the versions that the server supports
......@@ -38,7 +36,7 @@ var SupportedVersions = []VersionNumber{
// IsValidVersion says if the version is known to quic-go
func IsValidVersion(v VersionNumber) bool {
return v == VersionTLS || v == VersionMilestone0_10_0 || IsSupportedVersion(SupportedVersions, v)
return v == VersionTLS || IsSupportedVersion(SupportedVersions, v)
}
// UsesTLS says if this QUIC version uses TLS 1.3 for the handshake
......@@ -52,8 +50,6 @@ func (vn VersionNumber) String() string {
return "whatever"
case VersionUnknown:
return "unknown"
case VersionMilestone0_10_0:
return "quic-go Milestone 0.10.0"
case VersionTLS:
return "TLS dev version (WIP)"
default:
......
package utils
import "time"
import (
"math"
"time"
)
// A Timer wrapper that behaves correctly when resetting
type Timer struct {
......@@ -11,7 +14,7 @@ type Timer struct {
// NewTimer creates a new timer that is not set
func NewTimer() *Timer {
return &Timer{t: time.NewTimer(0)}
return &Timer{t: time.NewTimer(time.Duration(math.MaxInt64))}
}
// Chan returns the channel of the wrapped timer
......@@ -31,7 +34,9 @@ func (t *Timer) Reset(deadline time.Time) {
if !t.t.Stop() && !t.read {
<-t.t.C
}
t.t.Reset(time.Until(deadline))
if !deadline.IsZero() {
t.t.Reset(time.Until(deadline))
}
t.read = false
t.deadline = deadline
......
......@@ -19,23 +19,17 @@ type AckFrame struct {
DelayTime time.Duration
}
func parseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame, error) {
return parseAckOrAckEcnFrame(r, false, version)
}
func parseAckEcnFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame, error) {
return parseAckOrAckEcnFrame(r, true, version)
}
// parseAckFrame reads an ACK frame
func parseAckOrAckEcnFrame(r *bytes.Reader, ecn bool, version protocol.VersionNumber) (*AckFrame, error) {
func parseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame, error) {
if !version.UsesIETFFrameFormat() {
return parseAckFrameLegacy(r, version)
}
if _, err := r.ReadByte(); err != nil {
typeByte, err := r.ReadByte()
if err != nil {
return nil, err
}
ecn := typeByte&0x1 > 0
frame := &AckFrame{}
......@@ -50,14 +44,6 @@ func parseAckOrAckEcnFrame(r *bytes.Reader, ecn bool, version protocol.VersionNu
}
frame.DelayTime = time.Duration(delay*1<<ackDelayExponent) * time.Microsecond
if ecn {
for i := 0; i < 3; i++ {
if _, err := utils.ReadVarInt(r); err != nil {
return nil, err
}
}
}
numBlocks, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
......@@ -103,6 +89,16 @@ func parseAckOrAckEcnFrame(r *bytes.Reader, ecn bool, version protocol.VersionNu
if !frame.validateAckRanges() {
return nil, errInvalidAckRanges
}
// parse (and skip) the ECN section
if ecn {
for i := 0; i < 3; i++ {
if _, err := utils.ReadVarInt(r); err != nil {
return nil, err
}
}
}
return frame, nil
}
......@@ -112,7 +108,7 @@ func (f *AckFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error
return f.writeLegacy(b, version)
}
b.WriteByte(0x0d)
b.WriteByte(0x1a)
utils.WriteVarInt(b, uint64(f.LargestAcked()))
utils.WriteVarInt(b, encodeAckDelay(f.DelayTime))
......
......@@ -86,11 +86,6 @@ func parseIETFFrame(r *bytes.Reader, typeByte byte, v protocol.VersionNumber) (F
if err != nil {
err = qerr.Error(qerr.InvalidFrameData, err.Error())
}
case 0xd:
frame, err = parseAckFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidAckData, err.Error())
}
case 0xe:
frame, err = parsePathChallengeFrame(r, v)
if err != nil {
......@@ -101,8 +96,8 @@ func parseIETFFrame(r *bytes.Reader, typeByte byte, v protocol.VersionNumber) (F
if err != nil {
err = qerr.Error(qerr.InvalidFrameData, err.Error())
}
case 0x1a:
frame, err = parseAckEcnFrame(r, v)
case 0x1a, 0x1b:
frame, err = parseAckFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidAckData, err.Error())
}
......
......@@ -5,10 +5,13 @@ package quic
//go:generate sh -c "./mockgen_private.sh quic mock_send_stream_internal_test.go github.com/lucas-clemente/quic-go sendStreamI"
//go:generate sh -c "./mockgen_private.sh quic mock_stream_sender_test.go github.com/lucas-clemente/quic-go streamSender"
//go:generate sh -c "./mockgen_private.sh quic mock_stream_getter_test.go github.com/lucas-clemente/quic-go streamGetter"
//go:generate sh -c "./mockgen_private.sh quic mock_stream_frame_source_test.go github.com/lucas-clemente/quic-go streamFrameSource"
//go:generate sh -c "./mockgen_private.sh quic mock_frame_source_test.go github.com/lucas-clemente/quic-go frameSource"
//go:generate sh -c "./mockgen_private.sh quic mock_ack_frame_source_test.go github.com/lucas-clemente/quic-go ackFrameSource"
//go:generate sh -c "./mockgen_private.sh quic mock_crypto_stream_test.go github.com/lucas-clemente/quic-go cryptoStream"
//go:generate sh -c "./mockgen_private.sh quic mock_stream_manager_test.go github.com/lucas-clemente/quic-go streamManager"
//go:generate sh -c "./mockgen_private.sh quic mock_sealing_manager_test.go github.com/lucas-clemente/quic-go sealingManager"
//go:generate sh -c "./mockgen_private.sh quic mock_unpacker_test.go github.com/lucas-clemente/quic-go unpacker"
//go:generate sh -c "./mockgen_private.sh quic mock_packer_test.go github.com/lucas-clemente/quic-go packer"
//go:generate sh -c "./mockgen_private.sh quic mock_quic_aead_test.go github.com/lucas-clemente/quic-go quicAEAD"
//go:generate sh -c "./mockgen_private.sh quic mock_gquic_aead_test.go github.com/lucas-clemente/quic-go gQUICAEAD"
//go:generate sh -c "./mockgen_private.sh quic mock_session_runner_test.go github.com/lucas-clemente/quic-go sessionRunner"
......
......@@ -18,10 +18,12 @@ type packetNumberGenerator struct {
}
func newPacketNumberGenerator(initial, averagePeriod protocol.PacketNumber) *packetNumberGenerator {
return &packetNumberGenerator{
g := &packetNumberGenerator{
next: initial,
averagePeriod: averagePeriod,
}
g.generateNewSkip()
return g
}
func (p *packetNumberGenerator) Peek() protocol.PacketNumber {
......@@ -42,28 +44,19 @@ func (p *packetNumberGenerator) Pop() protocol.PacketNumber {
return next
}
func (p *packetNumberGenerator) generateNewSkip() error {
num, err := p.getRandomNumber()
if err != nil {
return err
}
func (p *packetNumberGenerator) generateNewSkip() {
num := p.getRandomNumber()
skip := protocol.PacketNumber(num) * (p.averagePeriod - 1) / (math.MaxUint16 / 2)
// make sure that there are never two consecutive packet numbers that are skipped
p.nextToSkip = p.next + 2 + skip
return nil
}
// getRandomNumber() generates a cryptographically secure random number between 0 and MaxUint16 (= 65535)
// The expectation value is 65535/2
func (p *packetNumberGenerator) getRandomNumber() (uint16, error) {
func (p *packetNumberGenerator) getRandomNumber() uint16 {
b := make([]byte, 2)
_, err := rand.Read(b)
if err != nil {
return 0, err
}
rand.Read(b) // ignore the error here
num := uint16(b[0])<<8 + uint16(b[1])
return num, nil
return num
}
......@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"net"
"sync"
"time"
"github.com/lucas-clemente/quic-go/internal/ackhandler"
......@@ -15,6 +14,16 @@ import (
"github.com/lucas-clemente/quic-go/internal/wire"
)
type packer interface {
PackPacket() (*packedPacket, error)
MaybePackAckPacket() (*packedPacket, error)
PackRetransmission(packet *ackhandler.Packet) ([]*packedPacket, error)
PackConnectionClose(*wire.ConnectionCloseFrame) (*packedPacket, error)
HandleTransportParameters(*handshake.TransportParameters)
ChangeDestConnectionID(protocol.ConnectionID)
}
type packedPacket struct {
header *wire.Header
raw []byte
......@@ -33,16 +42,37 @@ func (p *packedPacket) ToAckHandlerPacket() *ackhandler.Packet {
}
}
func getMaxPacketSize(addr net.Addr) protocol.ByteCount {
maxSize := protocol.ByteCount(protocol.MinInitialPacketSize)
// If this is not a UDP address, we don't know anything about the MTU.
// Use the minimum size of an Initial packet as the max packet size.
if udpAddr, ok := addr.(*net.UDPAddr); ok {
// If ip is not an IPv4 address, To4 returns nil.
// Note that there might be some corner cases, where this is not correct.
// See https://stackoverflow.com/questions/22751035/golang-distinguish-ipv4-ipv6.
if udpAddr.IP.To4() == nil {
maxSize = protocol.MaxPacketSizeIPv6
} else {
maxSize = protocol.MaxPacketSizeIPv4
}
}
return maxSize
}
type sealingManager interface {
GetSealer() (protocol.EncryptionLevel, handshake.Sealer)
GetSealerForCryptoStream() (protocol.EncryptionLevel, handshake.Sealer)
GetSealerWithEncryptionLevel(protocol.EncryptionLevel) (handshake.Sealer, error)
}
type streamFrameSource interface {
HasCryptoStreamData() bool
PopCryptoStreamFrame(protocol.ByteCount) *wire.StreamFrame
PopStreamFrames(protocol.ByteCount) []*wire.StreamFrame
type frameSource interface {
AppendStreamFrames([]wire.Frame, protocol.ByteCount) []wire.Frame
AppendControlFrames([]wire.Frame, protocol.ByteCount) ([]wire.Frame, protocol.ByteCount)
}
type ackFrameSource interface {
GetAckFrame() *wire.AckFrame
GetStopWaitingFrame(bool) *wire.StopWaitingFrame
}
type packetPacker struct {
......@@ -53,24 +83,21 @@ type packetPacker struct {
version protocol.VersionNumber
cryptoSetup sealingManager
token []byte
divNonce []byte
token []byte
packetNumberGenerator *packetNumberGenerator
getPacketNumberLen func(protocol.PacketNumber) protocol.PacketNumberLen
streams streamFrameSource
controlFrameMutex sync.Mutex
controlFrames []wire.Frame
cryptoStream cryptoStream
framer frameSource
acks ackFrameSource
stopWaiting *wire.StopWaitingFrame
ackFrame *wire.AckFrame
omitConnectionID bool
maxPacketSize protocol.ByteCount
hasSentPacket bool // has the packetPacker already sent a packet
numNonRetransmittableAcks int
}
var _ packer = &packetPacker{}
func newPacketPacker(
destConnID protocol.ConnectionID,
srcConnID protocol.ConnectionID,
......@@ -78,37 +105,26 @@ func newPacketPacker(
getPacketNumberLen func(protocol.PacketNumber) protocol.PacketNumberLen,
remoteAddr net.Addr, // only used for determining the max packet size
token []byte,
divNonce []byte,
cryptoStream cryptoStream,
cryptoSetup sealingManager,
streamFramer streamFrameSource,
framer frameSource,
acks ackFrameSource,
perspective protocol.Perspective,
version protocol.VersionNumber,
) *packetPacker {
maxPacketSize := protocol.ByteCount(protocol.MinInitialPacketSize)
// If this is not a UDP address, we don't know anything about the MTU.
// Use the minimum size of an Initial packet as the max packet size.
if udpAddr, ok := remoteAddr.(*net.UDPAddr); ok {
// If ip is not an IPv4 address, To4 returns nil.
// Note that there might be some corner cases, where this is not correct.
// See https://stackoverflow.com/questions/22751035/golang-distinguish-ipv4-ipv6.
if udpAddr.IP.To4() == nil {
maxPacketSize = protocol.MaxPacketSizeIPv6
} else {
maxPacketSize = protocol.MaxPacketSizeIPv4
}
}
return &packetPacker{
cryptoStream: cryptoStream,
cryptoSetup: cryptoSetup,
divNonce: divNonce,
token: token,
destConnID: destConnID,
srcConnID: srcConnID,
perspective: perspective,
version: version,
streams: streamFramer,
framer: framer,
acks: acks,
getPacketNumberLen: getPacketNumberLen,
packetNumberGenerator: newPacketNumberGenerator(initialPacketNumber, protocol.SkipPacketAveragePeriodLength),
maxPacketSize: maxPacketSize,
maxPacketSize: getMaxPacketSize(remoteAddr),
}
}
......@@ -126,20 +142,14 @@ func (p *packetPacker) PackConnectionClose(ccf *wire.ConnectionCloseFrame) (*pac
}, err
}
func (p *packetPacker) PackAckPacket() (*packedPacket, error) {
if p.ackFrame == nil {
return nil, errors.New("packet packer BUG: no ack frame queued")
func (p *packetPacker) MaybePackAckPacket() (*packedPacket, error) {
ack := p.acks.GetAckFrame()
if ack == nil {
return nil, nil
}
encLevel, sealer := p.cryptoSetup.GetSealer()
header := p.getHeader(encLevel)
frames := []wire.Frame{p.ackFrame}
if p.stopWaiting != nil { // a STOP_WAITING will only be queued when using gQUIC
p.stopWaiting.PacketNumber = header.PacketNumber
p.stopWaiting.PacketNumberLen = header.PacketNumberLen
frames = append(frames, p.stopWaiting)
p.stopWaiting = nil
}
p.ackFrame = nil
frames := []wire.Frame{ack}
raw, err := p.writeAndSealPacket(header, frames, sealer)
return &packedPacket{
header: header,
......@@ -173,7 +183,7 @@ func (p *packetPacker) PackRetransmission(packet *ackhandler.Packet) ([]*packedP
encLevel, sealer := p.cryptoSetup.GetSealer()
for len(controlFrames) > 0 || len(streamFrames) > 0 {
var frames []wire.Frame
var payloadLength protocol.ByteCount
var length protocol.ByteCount
header := p.getHeader(encLevel)
headerLength, err := header.GetLength(p.version)
......@@ -182,48 +192,23 @@ func (p *packetPacker) PackRetransmission(packet *ackhandler.Packet) ([]*packedP
}
maxSize := p.maxPacketSize - protocol.ByteCount(sealer.Overhead()) - headerLength
// for gQUIC: add a STOP_WAITING for *every* retransmission
if p.version.UsesStopWaitingFrames() {
if p.stopWaiting == nil {
return nil, errors.New("PacketPacker BUG: Handshake retransmissions must contain a STOP_WAITING frame")
}
// create a new StopWaitingFrame, since we might need to send more than one packet as a retransmission
swf := &wire.StopWaitingFrame{
LeastUnacked: p.stopWaiting.LeastUnacked,
PacketNumber: header.PacketNumber,
PacketNumberLen: header.PacketNumberLen,
}
payloadLength += swf.Length(p.version)
frames = append(frames, swf)
}
for len(controlFrames) > 0 {
frame := controlFrames[0]
length := frame.Length(p.version)
if payloadLength+length > maxSize {
frameLen := frame.Length(p.version)
if length+frameLen > maxSize {
break
}
payloadLength += length
length += frameLen
frames = append(frames, frame)
controlFrames = controlFrames[1:]
}
// temporarily increase the maxFrameSize by the (minimum) length of the DataLen field
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with StreamFrames that have the DataLen set
// however, for the last STREAM frame in the packet, we can omit the DataLen, thus yielding a packet of exactly the correct size
// for gQUIC STREAM frames, DataLen is always 2 bytes
// for IETF draft style STREAM frames, the length is encoded to either 1 or 2 bytes
if p.version.UsesIETFFrameFormat() {
maxSize++
} else {
maxSize += 2
}
for len(streamFrames) > 0 && payloadLength+protocol.MinStreamFrameSize < maxSize {
// TODO: optimize by setting DataLenPresent = false on all but the last STREAM frame
for len(streamFrames) > 0 && length+protocol.MinStreamFrameSize < maxSize {
frame := streamFrames[0]
frame.DataLenPresent = false
frameToAdd := frame
sf, err := frame.MaybeSplitOffFrame(maxSize-payloadLength, p.version)
sf, err := frame.MaybeSplitOffFrame(maxSize-length, p.version)
if err != nil {
return nil, err
}
......@@ -232,7 +217,8 @@ func (p *packetPacker) PackRetransmission(packet *ackhandler.Packet) ([]*packedP
} else {
streamFrames = streamFrames[1:]
}
payloadLength += frameToAdd.Length(p.version)
frame.DataLenPresent = true
length += frameToAdd.Length(p.version)
frames = append(frames, frameToAdd)
}
if sf, ok := frames[len(frames)-1].(*wire.StreamFrame); ok {
......@@ -249,7 +235,6 @@ func (p *packetPacker) PackRetransmission(packet *ackhandler.Packet) ([]*packedP
encryptionLevel: encLevel,
})
}
p.stopWaiting = nil
return packets, nil
}
......@@ -265,24 +250,11 @@ func (p *packetPacker) packHandshakeRetransmission(packet *ackhandler.Packet) (*
}
header := p.getHeader(packet.EncryptionLevel)
header.Type = packet.PacketType
var frames []wire.Frame
if p.version.UsesStopWaitingFrames() { // for gQUIC: pack a STOP_WAITING first
if p.stopWaiting == nil {
return nil, errors.New("PacketPacker BUG: Handshake retransmissions must contain a STOP_WAITING frame")
}
swf := p.stopWaiting
swf.PacketNumber = header.PacketNumber
swf.PacketNumberLen = header.PacketNumberLen
p.stopWaiting = nil
frames = append([]wire.Frame{swf}, packet.Frames...)
} else {
frames = packet.Frames
}
raw, err := p.writeAndSealPacket(header, frames, sealer)
raw, err := p.writeAndSealPacket(header, packet.Frames, sealer)
return &packedPacket{
header: header,
raw: raw,
frames: frames,
frames: packet.Frames,
encryptionLevel: packet.EncryptionLevel,
}, err
}
......@@ -290,14 +262,17 @@ func (p *packetPacker) packHandshakeRetransmission(packet *ackhandler.Packet) (*
// PackPacket packs a new packet
// the other controlFrames are sent in the next packet, but might be queued and sent in the next packet if the packet would overflow MaxPacketSize otherwise
func (p *packetPacker) PackPacket() (*packedPacket, error) {
hasCryptoStreamFrame := p.streams.HasCryptoStreamData()
packet, err := p.maybePackCryptoPacket()
if err != nil {
return nil, err
}
if packet != nil {
return packet, nil
}
// if this is the first packet to be send, make sure it contains stream data
if !p.hasSentPacket && !hasCryptoStreamFrame {
if !p.hasSentPacket && packet == nil {
return nil, nil
}
if hasCryptoStreamFrame {
return p.packCryptoPacket()
}
encLevel, sealer := p.cryptoSetup.GetSealer()
......@@ -306,54 +281,45 @@ func (p *packetPacker) PackPacket() (*packedPacket, error) {
if err != nil {
return nil, err
}
if p.stopWaiting != nil {
p.stopWaiting.PacketNumber = header.PacketNumber
p.stopWaiting.PacketNumberLen = header.PacketNumberLen
}
maxSize := p.maxPacketSize - protocol.ByteCount(sealer.Overhead()) - headerLength
payloadFrames, err := p.composeNextPacket(maxSize, p.canSendData(encLevel))
frames, err := p.composeNextPacket(maxSize, p.canSendData(encLevel))
if err != nil {
return nil, err
}
// Check if we have enough frames to send
if len(payloadFrames) == 0 {
return nil, nil
}
// Don't send out packets that only contain a StopWaitingFrame
if len(payloadFrames) == 1 && p.stopWaiting != nil {
if len(frames) == 0 {
return nil, nil
}
if p.ackFrame != nil {
// check if this packet only contains an ACK (and maybe a STOP_WAITING)
if len(payloadFrames) == 1 || (p.stopWaiting != nil && len(payloadFrames) == 2) {
if p.numNonRetransmittableAcks >= protocol.MaxNonRetransmittableAcks {
payloadFrames = append(payloadFrames, &wire.PingFrame{})
p.numNonRetransmittableAcks = 0
} else {
p.numNonRetransmittableAcks++
}
} else {
// check if this packet only contains an ACK
if !ackhandler.HasRetransmittableFrames(frames) {
if p.numNonRetransmittableAcks >= protocol.MaxNonRetransmittableAcks {
frames = append(frames, &wire.PingFrame{})
p.numNonRetransmittableAcks = 0
} else {
p.numNonRetransmittableAcks++
}
} else {
p.numNonRetransmittableAcks = 0
}
p.stopWaiting = nil
p.ackFrame = nil
raw, err := p.writeAndSealPacket(header, payloadFrames, sealer)
raw, err := p.writeAndSealPacket(header, frames, sealer)
if err != nil {
return nil, err
}
return &packedPacket{
header: header,
raw: raw,
frames: payloadFrames,
frames: frames,
encryptionLevel: encLevel,
}, nil
}
func (p *packetPacker) packCryptoPacket() (*packedPacket, error) {
func (p *packetPacker) maybePackCryptoPacket() (*packedPacket, error) {
if !p.cryptoStream.hasData() {
return nil, nil
}
encLevel, sealer := p.cryptoSetup.GetSealerForCryptoStream()
header := p.getHeader(encLevel)
headerLength, err := header.GetLength(p.version)
......@@ -361,7 +327,7 @@ func (p *packetPacker) packCryptoPacket() (*packedPacket, error) {
return nil, err
}
maxLen := p.maxPacketSize - protocol.ByteCount(sealer.Overhead()) - protocol.NonForwardSecurePacketSizeReduction - headerLength
sf := p.streams.PopCryptoStreamFrame(maxLen)
sf, _ := p.cryptoStream.popStreamFrame(maxLen)
sf.DataLenPresent = false
frames := []wire.Frame{sf}
raw, err := p.writeAndSealPacket(header, frames, sealer)
......@@ -380,74 +346,37 @@ func (p *packetPacker) composeNextPacket(
maxFrameSize protocol.ByteCount,
canSendStreamFrames bool,
) ([]wire.Frame, error) {
var payloadLength protocol.ByteCount
var payloadFrames []wire.Frame
// STOP_WAITING and ACK will always fit
if p.ackFrame != nil { // ACKs need to go first, so that the sentPacketHandler will recognize them
payloadFrames = append(payloadFrames, p.ackFrame)
l := p.ackFrame.Length(p.version)
payloadLength += l
}
if p.stopWaiting != nil { // a STOP_WAITING will only be queued when using gQUIC
payloadFrames = append(payloadFrames, p.stopWaiting)
payloadLength += p.stopWaiting.Length(p.version)
}
p.controlFrameMutex.Lock()
for len(p.controlFrames) > 0 {
frame := p.controlFrames[len(p.controlFrames)-1]
length := frame.Length(p.version)
if payloadLength+length > maxFrameSize {
break
}
payloadFrames = append(payloadFrames, frame)
payloadLength += length
p.controlFrames = p.controlFrames[:len(p.controlFrames)-1]
}
p.controlFrameMutex.Unlock()
var length protocol.ByteCount
var frames []wire.Frame
if payloadLength > maxFrameSize {
return nil, fmt.Errorf("Packet Packer BUG: packet payload (%d) too large (%d)", payloadLength, maxFrameSize)
// ACKs need to go first, so that the sentPacketHandler will recognize them
if ack := p.acks.GetAckFrame(); ack != nil {
frames = append(frames, ack)
length += ack.Length(p.version)
}
var lengthAdded protocol.ByteCount
frames, lengthAdded = p.framer.AppendControlFrames(frames, maxFrameSize-length)
length += lengthAdded
if !canSendStreamFrames {
return payloadFrames, nil
return frames, nil
}
// temporarily increase the maxFrameSize by the (minimum) length of the DataLen field
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with StreamFrames that have the DataLen set
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with STREAM frames that have the DataLen set
// however, for the last STREAM frame in the packet, we can omit the DataLen, thus yielding a packet of exactly the correct size
// for gQUIC STREAM frames, DataLen is always 2 bytes
// for IETF draft style STREAM frames, the length is encoded to either 1 or 2 bytes
if p.version.UsesIETFFrameFormat() {
maxFrameSize++
} else {
maxFrameSize += 2
}
fs := p.streams.PopStreamFrames(maxFrameSize - payloadLength)
if len(fs) != 0 {
fs[len(fs)-1].DataLenPresent = false
}
// the length is encoded to either 1 or 2 bytes
maxFrameSize++
for _, f := range fs {
payloadFrames = append(payloadFrames, f)
}
return payloadFrames, nil
}
func (p *packetPacker) QueueControlFrame(frame wire.Frame) {
switch f := frame.(type) {
case *wire.StopWaitingFrame:
p.stopWaiting = f
case *wire.AckFrame:
p.ackFrame = f
default:
p.controlFrameMutex.Lock()
p.controlFrames = append(p.controlFrames, f)
p.controlFrameMutex.Unlock()
frames = p.framer.AppendStreamFrames(frames, maxFrameSize-length)
if len(frames) > 0 {
lastFrame := frames[len(frames)-1]
if sf, ok := lastFrame.(*wire.StreamFrame); ok {
sf.DataLenPresent = false
}
}
return frames, nil
}
func (p *packetPacker) getHeader(encLevel protocol.EncryptionLevel) *wire.Header {
......@@ -455,17 +384,15 @@ func (p *packetPacker) getHeader(encLevel protocol.EncryptionLevel) *wire.Header
packetNumberLen := p.getPacketNumberLen(pnum)
header := &wire.Header{
PacketNumber: pnum,
PacketNumberLen: packetNumberLen,
Version: p.version,
PacketNumber: pnum,
PacketNumberLen: packetNumberLen,
Version: p.version,
DestConnectionID: p.destConnID,
}
if p.version.UsesIETFHeaderFormat() && encLevel != protocol.EncryptionForwardSecure {
if encLevel != protocol.EncryptionForwardSecure {
header.IsLongHeader = true
header.SrcConnectionID = p.srcConnID
if !p.version.UsesVarintPacketNumbers() {
header.PacketNumberLen = protocol.PacketNumberLen4
}
// Set the payload len to maximum size.
// Since it is encoded as a varint, this guarantees us that the header will end up at most as big as GetLength() returns.
header.PayloadLen = p.maxPacketSize
......@@ -477,24 +404,12 @@ func (p *packetPacker) getHeader(encLevel protocol.EncryptionLevel) *wire.Header
}
}
if !p.omitConnectionID || encLevel != protocol.EncryptionForwardSecure {
header.DestConnectionID = p.destConnID
}
if !p.version.UsesTLS() {
if p.perspective == protocol.PerspectiveServer && encLevel == protocol.EncryptionSecure {
header.Type = protocol.PacketType0RTT
header.DiversificationNonce = p.divNonce
}
if p.perspective == protocol.PerspectiveClient && encLevel != protocol.EncryptionForwardSecure {
header.VersionFlag = true
}
}
return header
}
func (p *packetPacker) writeAndSealPacket(
header *wire.Header,
payloadFrames []wire.Frame,
frames []wire.Frame,
sealer handshake.Sealer,
) ([]byte, error) {
raw := *getPacketBuffer()
......@@ -507,7 +422,7 @@ func (p *packetPacker) writeAndSealPacket(
header.PayloadLen = protocol.ByteCount(protocol.MinInitialPacketSize) - headerLen
} else {
payloadLen := protocol.ByteCount(sealer.Overhead())
for _, frame := range payloadFrames {
for _, frame := range frames {
payloadLen += frame.Length(p.version)
}
header.PayloadLen = payloadLen
......@@ -521,18 +436,17 @@ func (p *packetPacker) writeAndSealPacket(
// the Initial packet needs to be padded, so the last STREAM frame must have the data length present
if header.Type == protocol.PacketTypeInitial {
lastFrame := payloadFrames[len(payloadFrames)-1]
lastFrame := frames[len(frames)-1]
if sf, ok := lastFrame.(*wire.StreamFrame); ok {
sf.DataLenPresent = true
}
}
for _, frame := range payloadFrames {
for _, frame := range frames {
if err := frame.Write(buffer, p.version); err != nil {
return nil, err
}
}
// if this is an IETF QUIC Initial packet, we need to pad it to fulfill the minimum size requirement
// in gQUIC, padding is handled in the CHLO
// if this is an Initial packet, we need to pad it to fulfill the minimum size requirement
if header.Type == protocol.PacketTypeInitial {
paddingLen := protocol.MinInitialPacketSize - sealer.Overhead() - buffer.Len()
if paddingLen > 0 {
......@@ -563,14 +477,12 @@ func (p *packetPacker) canSendData(encLevel protocol.EncryptionLevel) bool {
return encLevel == protocol.EncryptionForwardSecure
}
func (p *packetPacker) SetOmitConnectionID() {
p.omitConnectionID = true
}
func (p *packetPacker) ChangeDestConnectionID(connID protocol.ConnectionID) {
p.destConnID = connID
}
func (p *packetPacker) SetMaxPacketSize(size protocol.ByteCount) {
p.maxPacketSize = utils.MinByteCount(p.maxPacketSize, size)
func (p *packetPacker) HandleTransportParameters(params *handshake.TransportParameters) {
if params.MaxPacketSize != 0 {
p.maxPacketSize = utils.MinByteCount(p.maxPacketSize, params.MaxPacketSize)
}
}
package quic
import (
"bytes"
"errors"
"fmt"
"net"
"github.com/lucas-clemente/quic-go/internal/ackhandler"
"github.com/lucas-clemente/quic-go/internal/handshake"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
)
// sentAndReceivedPacketManager is only needed until STOP_WAITING is removed
type sentAndReceivedPacketManager struct {
ackhandler.SentPacketHandler
ackhandler.ReceivedPacketHandler
}
var _ ackFrameSource = &sentAndReceivedPacketManager{}
type packetPackerLegacy struct {
destConnID protocol.ConnectionID
srcConnID protocol.ConnectionID
perspective protocol.Perspective
version protocol.VersionNumber
cryptoSetup sealingManager
divNonce []byte
packetNumberGenerator *packetNumberGenerator
getPacketNumberLen func(protocol.PacketNumber) protocol.PacketNumberLen
cryptoStream cryptoStream
framer frameSource
acks ackFrameSource
omitConnectionID bool
maxPacketSize protocol.ByteCount
hasSentPacket bool // has the packetPacker already sent a packet
numNonRetransmittableAcks int
}
var _ packer = &packetPackerLegacy{}
func newPacketPackerLegacy(
destConnID protocol.ConnectionID,
srcConnID protocol.ConnectionID,
getPacketNumberLen func(protocol.PacketNumber) protocol.PacketNumberLen,
remoteAddr net.Addr, // only used for determining the max packet size
divNonce []byte,
cryptoStream cryptoStream,
cryptoSetup sealingManager,
framer frameSource,
acks ackFrameSource,
perspective protocol.Perspective,
version protocol.VersionNumber,
) *packetPackerLegacy {
return &packetPackerLegacy{
cryptoStream: cryptoStream,
cryptoSetup: cryptoSetup,
divNonce: divNonce,
destConnID: destConnID,
srcConnID: srcConnID,
perspective: perspective,
version: version,
framer: framer,
acks: acks,
getPacketNumberLen: getPacketNumberLen,
packetNumberGenerator: newPacketNumberGenerator(1, protocol.SkipPacketAveragePeriodLength),
maxPacketSize: getMaxPacketSize(remoteAddr),
}
}
// PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame
func (p *packetPackerLegacy) PackConnectionClose(ccf *wire.ConnectionCloseFrame) (*packedPacket, error) {
frames := []wire.Frame{ccf}
encLevel, sealer := p.cryptoSetup.GetSealer()
header := p.getHeader(encLevel)
raw, err := p.writeAndSealPacket(header, frames, sealer)
return &packedPacket{
header: header,
raw: raw,
frames: frames,
encryptionLevel: encLevel,
}, err
}
func (p *packetPackerLegacy) MaybePackAckPacket() (*packedPacket, error) {
ack := p.acks.GetAckFrame()
if ack == nil {
return nil, nil
}
encLevel, sealer := p.cryptoSetup.GetSealer()
header := p.getHeader(encLevel)
frames := []wire.Frame{ack}
// add a STOP_WAITING frame, if necessary
if p.version.UsesStopWaitingFrames() {
if swf := p.acks.GetStopWaitingFrame(false); swf != nil {
swf.PacketNumber = header.PacketNumber
swf.PacketNumberLen = header.PacketNumberLen
frames = append(frames, swf)
}
}
raw, err := p.writeAndSealPacket(header, frames, sealer)
return &packedPacket{
header: header,
raw: raw,
frames: frames,
encryptionLevel: encLevel,
}, err
}
// PackRetransmission packs a retransmission
// For packets sent after completion of the handshake, it might happen that 2 packets have to be sent.
// This can happen e.g. when a longer packet number is used in the header.
func (p *packetPackerLegacy) PackRetransmission(packet *ackhandler.Packet) ([]*packedPacket, error) {
if packet.EncryptionLevel != protocol.EncryptionForwardSecure {
p, err := p.packHandshakeRetransmission(packet)
return []*packedPacket{p}, err
}
var controlFrames []wire.Frame
var streamFrames []*wire.StreamFrame
for _, f := range packet.Frames {
if sf, ok := f.(*wire.StreamFrame); ok {
sf.DataLenPresent = true
streamFrames = append(streamFrames, sf)
} else {
controlFrames = append(controlFrames, f)
}
}
var packets []*packedPacket
encLevel, sealer := p.cryptoSetup.GetSealer()
var swf *wire.StopWaitingFrame
// add a STOP_WAITING for *every* retransmission
if p.version.UsesStopWaitingFrames() {
swf = p.acks.GetStopWaitingFrame(true)
}
for len(controlFrames) > 0 || len(streamFrames) > 0 {
var frames []wire.Frame
var length protocol.ByteCount
header := p.getHeader(encLevel)
headerLength, err := header.GetLength(p.version)
if err != nil {
return nil, err
}
maxSize := p.maxPacketSize - protocol.ByteCount(sealer.Overhead()) - headerLength
if p.version.UsesStopWaitingFrames() {
// create a new STOP_WAIITNG Frame, since we might need to send more than one packet as a retransmission
stopWaitingFrame := &wire.StopWaitingFrame{
LeastUnacked: swf.LeastUnacked,
PacketNumber: header.PacketNumber,
PacketNumberLen: header.PacketNumberLen,
}
length += stopWaitingFrame.Length(p.version)
frames = append(frames, stopWaitingFrame)
}
for len(controlFrames) > 0 {
frame := controlFrames[0]
frameLen := frame.Length(p.version)
if length+frameLen > maxSize {
break
}
length += frameLen
frames = append(frames, frame)
controlFrames = controlFrames[1:]
}
// temporarily increase the maxFrameSize by the (minimum) length of the DataLen field
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with StreamFrames that have the DataLen set
// however, for the last STREAM frame in the packet, we can omit the DataLen, thus yielding a packet of exactly the correct size
maxSize += 2
for len(streamFrames) > 0 && length+protocol.MinStreamFrameSize < maxSize {
frame := streamFrames[0]
frameToAdd := frame
sf, err := frame.MaybeSplitOffFrame(maxSize-length, p.version)
if err != nil {
return nil, err
}
if sf != nil {
frameToAdd = sf
} else {
streamFrames = streamFrames[1:]
}
length += frameToAdd.Length(p.version)
frames = append(frames, frameToAdd)
}
if sf, ok := frames[len(frames)-1].(*wire.StreamFrame); ok {
sf.DataLenPresent = false
}
raw, err := p.writeAndSealPacket(header, frames, sealer)
if err != nil {
return nil, err
}
packets = append(packets, &packedPacket{
header: header,
raw: raw,
frames: frames,
encryptionLevel: encLevel,
})
}
return packets, nil
}
// packHandshakeRetransmission retransmits a handshake packet, that was sent with less than forward-secure encryption
func (p *packetPackerLegacy) packHandshakeRetransmission(packet *ackhandler.Packet) (*packedPacket, error) {
sealer, err := p.cryptoSetup.GetSealerWithEncryptionLevel(packet.EncryptionLevel)
if err != nil {
return nil, err
}
// make sure that the retransmission for an Initial packet is sent as an Initial packet
if packet.PacketType == protocol.PacketTypeInitial {
p.hasSentPacket = false
}
header := p.getHeader(packet.EncryptionLevel)
header.Type = packet.PacketType
var frames []wire.Frame
if p.version.UsesStopWaitingFrames() { // pack a STOP_WAITING first
swf := p.acks.GetStopWaitingFrame(true)
swf.PacketNumber = header.PacketNumber
swf.PacketNumberLen = header.PacketNumberLen
frames = append([]wire.Frame{swf}, packet.Frames...)
} else {
frames = packet.Frames
}
raw, err := p.writeAndSealPacket(header, frames, sealer)
return &packedPacket{
header: header,
raw: raw,
frames: frames,
encryptionLevel: packet.EncryptionLevel,
}, err
}
// PackPacket packs a new packet
// the other controlFrames are sent in the next packet, but might be queued and sent in the next packet if the packet would overflow MaxPacketSize otherwise
func (p *packetPackerLegacy) PackPacket() (*packedPacket, error) {
packet, err := p.maybePackCryptoPacket()
if err != nil {
return nil, err
}
if packet != nil {
return packet, nil
}
// if this is the first packet to be send, make sure it contains stream data
if !p.hasSentPacket && packet == nil {
return nil, nil
}
encLevel, sealer := p.cryptoSetup.GetSealer()
header := p.getHeader(encLevel)
headerLength, err := header.GetLength(p.version)
if err != nil {
return nil, err
}
maxSize := p.maxPacketSize - protocol.ByteCount(sealer.Overhead()) - headerLength
frames, err := p.composeNextPacket(header, maxSize, p.canSendData(encLevel))
if err != nil {
return nil, err
}
// Check if we have enough frames to send
if len(frames) == 0 {
return nil, nil
}
// check if this packet only contains an ACK (and maybe a STOP_WAITING)
if !ackhandler.HasRetransmittableFrames(frames) {
if p.numNonRetransmittableAcks >= protocol.MaxNonRetransmittableAcks {
frames = append(frames, &wire.PingFrame{})
p.numNonRetransmittableAcks = 0
} else {
p.numNonRetransmittableAcks++
}
} else {
p.numNonRetransmittableAcks = 0
}
raw, err := p.writeAndSealPacket(header, frames, sealer)
if err != nil {
return nil, err
}
return &packedPacket{
header: header,
raw: raw,
frames: frames,
encryptionLevel: encLevel,
}, nil
}
func (p *packetPackerLegacy) maybePackCryptoPacket() (*packedPacket, error) {
if !p.cryptoStream.hasData() {
return nil, nil
}
encLevel, sealer := p.cryptoSetup.GetSealerForCryptoStream()
header := p.getHeader(encLevel)
headerLength, err := header.GetLength(p.version)
if err != nil {
return nil, err
}
maxLen := p.maxPacketSize - protocol.ByteCount(sealer.Overhead()) - protocol.NonForwardSecurePacketSizeReduction - headerLength
sf, _ := p.cryptoStream.popStreamFrame(maxLen)
sf.DataLenPresent = false
frames := []wire.Frame{sf}
raw, err := p.writeAndSealPacket(header, frames, sealer)
if err != nil {
return nil, err
}
return &packedPacket{
header: header,
raw: raw,
frames: frames,
encryptionLevel: encLevel,
}, nil
}
func (p *packetPackerLegacy) composeNextPacket(
header *wire.Header, // only needed to fill in the STOP_WAITING frame
maxFrameSize protocol.ByteCount,
canSendStreamFrames bool,
) ([]wire.Frame, error) {
var length protocol.ByteCount
var frames []wire.Frame
// STOP_WAITING and ACK will always fit
// ACKs need to go first, so that the sentPacketHandler will recognize them
if ack := p.acks.GetAckFrame(); ack != nil {
frames = append(frames, ack)
length += ack.Length(p.version)
// add a STOP_WAITING, for gQUIC
if p.version.UsesStopWaitingFrames() {
if swf := p.acks.GetStopWaitingFrame(false); swf != nil {
swf.PacketNumber = header.PacketNumber
swf.PacketNumberLen = header.PacketNumberLen
frames = append(frames, swf)
length += swf.Length(p.version)
}
}
}
var lengthAdded protocol.ByteCount
frames, lengthAdded = p.framer.AppendControlFrames(frames, maxFrameSize-length)
length += lengthAdded
if !canSendStreamFrames {
return frames, nil
}
// temporarily increase the maxFrameSize by the (minimum) length of the DataLen field
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with StreamFrames that have the DataLen set
// however, for the last STREAM frame in the packet, we can omit the DataLen, thus yielding a packet of exactly the correct size
maxFrameSize += 2
frames = p.framer.AppendStreamFrames(frames, maxFrameSize-length)
if len(frames) > 0 {
lastFrame := frames[len(frames)-1]
if sf, ok := lastFrame.(*wire.StreamFrame); ok {
sf.DataLenPresent = false
}
}
return frames, nil
}
func (p *packetPackerLegacy) getHeader(encLevel protocol.EncryptionLevel) *wire.Header {
pnum := p.packetNumberGenerator.Peek()
packetNumberLen := p.getPacketNumberLen(pnum)
header := &wire.Header{
PacketNumber: pnum,
PacketNumberLen: packetNumberLen,
Version: p.version,
}
if p.version.UsesIETFHeaderFormat() && encLevel != protocol.EncryptionForwardSecure {
header.IsLongHeader = true
header.SrcConnectionID = p.srcConnID
header.PacketNumberLen = protocol.PacketNumberLen4
if !p.hasSentPacket && p.perspective == protocol.PerspectiveClient {
header.Type = protocol.PacketTypeInitial
} else {
header.Type = protocol.PacketTypeHandshake
}
}
if !p.omitConnectionID || encLevel != protocol.EncryptionForwardSecure {
header.DestConnectionID = p.destConnID
}
if p.perspective == protocol.PerspectiveServer && encLevel == protocol.EncryptionSecure {
header.Type = protocol.PacketType0RTT
header.DiversificationNonce = p.divNonce
}
if p.perspective == protocol.PerspectiveClient && encLevel != protocol.EncryptionForwardSecure {
header.VersionFlag = true
}
return header
}
func (p *packetPackerLegacy) writeAndSealPacket(
header *wire.Header,
frames []wire.Frame,
sealer handshake.Sealer,
) ([]byte, error) {
raw := *getPacketBuffer()
buffer := bytes.NewBuffer(raw[:0])
if err := header.Write(buffer, p.perspective, p.version); err != nil {
return nil, err
}
payloadStartIndex := buffer.Len()
for _, frame := range frames {
if err := frame.Write(buffer, p.version); err != nil {
return nil, err
}
}
if size := protocol.ByteCount(buffer.Len() + sealer.Overhead()); size > p.maxPacketSize {
return nil, fmt.Errorf("PacketPacker BUG: packet too large (%d bytes, allowed %d bytes)", size, p.maxPacketSize)
}
raw = raw[0:buffer.Len()]
_ = sealer.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], header.PacketNumber, raw[:payloadStartIndex])
raw = raw[0 : buffer.Len()+sealer.Overhead()]
num := p.packetNumberGenerator.Pop()
if num != header.PacketNumber {
return nil, errors.New("packetPacker BUG: Peeked and Popped packet numbers do not match")
}
p.hasSentPacket = true
return raw, nil
}
func (p *packetPackerLegacy) canSendData(encLevel protocol.EncryptionLevel) bool {
if p.perspective == protocol.PerspectiveClient {
return encLevel >= protocol.EncryptionSecure
}
return encLevel == protocol.EncryptionForwardSecure
}
func (p *packetPackerLegacy) ChangeDestConnectionID(connID protocol.ConnectionID) {
panic("changing connection IDs not supported by gQUIC")
}
func (p *packetPackerLegacy) HandleTransportParameters(params *handshake.TransportParameters) {
p.omitConnectionID = params.OmitConnectionID
}
......@@ -8,6 +8,7 @@ import (
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
)
......@@ -43,8 +44,8 @@ type receiveStream struct {
canceledRead bool // set when CancelRead() is called
resetRemotely bool // set when HandleRstStreamFrame() is called
readChan chan struct{}
readDeadline time.Time
readChan chan struct{}
deadline time.Time
flowController flowcontrol.StreamFlowController
version protocol.VersionNumber
......@@ -108,6 +109,7 @@ func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, err
return false, bytesRead, s.closeForShutdownErr
}
var deadlineTimer *utils.Timer
for {
// Stop waiting on errors
if s.closedForShutdown {
......@@ -120,9 +122,15 @@ func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, err
return false, bytesRead, s.resetRemotelyErr
}
deadline := s.readDeadline
if !deadline.IsZero() && !time.Now().Before(deadline) {
return false, bytesRead, errDeadline
deadline := s.deadline
if !deadline.IsZero() {
if !time.Now().Before(deadline) {
return false, bytesRead, errDeadline
}
if deadlineTimer == nil {
deadlineTimer = utils.NewTimer()
}
deadlineTimer.Reset(deadline)
}
if s.currentFrame != nil || s.currentFrameIsLast {
......@@ -135,7 +143,8 @@ func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, err
} else {
select {
case <-s.readChan:
case <-time.After(time.Until(deadline)):
case <-deadlineTimer.Chan():
deadlineTimer.SetRead()
}
}
s.mutex.Lock()
......@@ -164,7 +173,9 @@ func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, err
s.flowController.AddBytesRead(protocol.ByteCount(m))
}
// increase the flow control window, if necessary
s.flowController.MaybeQueueWindowUpdate()
if s.streamID != s.version.CryptoStreamID() {
s.flowController.MaybeQueueWindowUpdate()
}
if s.readPosInFrame >= len(s.currentFrame) && s.currentFrameIsLast {
s.finRead = true
......@@ -270,13 +281,9 @@ func (s *receiveStream) onClose(offset protocol.ByteCount) {
func (s *receiveStream) SetReadDeadline(t time.Time) error {
s.mutex.Lock()
oldDeadline := s.readDeadline
s.readDeadline = t
s.deadline = t
s.mutex.Unlock()
// if the new deadline is before the currently set deadline, wake up Read()
if t.Before(oldDeadline) {
s.signalRead()
}
s.signalRead()
return nil
}
......
......@@ -15,6 +15,7 @@ import (
type sendStreamI interface {
SendStream
handleStopSendingFrame(*wire.StopSendingFrame)
hasData() bool
popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool)
closeForShutdown(error)
handleMaxStreamDataFrame(*wire.MaxStreamDataFrame)
......@@ -40,8 +41,9 @@ type sendStream struct {
finSent bool // set when a STREAM_FRAME with FIN bit has b
dataForWriting []byte
writeChan chan struct{}
writeDeadline time.Time
writeChan chan struct{}
deadline time.Time
flowController flowcontrol.StreamFlowController
......@@ -85,49 +87,60 @@ func (s *sendStream) Write(p []byte) (int, error) {
if s.closeForShutdownErr != nil {
return 0, s.closeForShutdownErr
}
if !s.writeDeadline.IsZero() && !time.Now().Before(s.writeDeadline) {
if !s.deadline.IsZero() && !time.Now().Before(s.deadline) {
return 0, errDeadline
}
if len(p) == 0 {
return 0, nil
}
s.dataForWriting = make([]byte, len(p))
copy(s.dataForWriting, p)
s.sender.onHasStreamData(s.streamID)
s.dataForWriting = p
var bytesWritten int
var err error
var (
deadlineTimer *utils.Timer
bytesWritten int
notifiedSender bool
)
for {
bytesWritten = len(p) - len(s.dataForWriting)
deadline := s.writeDeadline
if !deadline.IsZero() && !time.Now().Before(deadline) {
s.dataForWriting = nil
err = errDeadline
break
deadline := s.deadline
if !deadline.IsZero() {
if !time.Now().Before(deadline) {
s.dataForWriting = nil
return bytesWritten, errDeadline
}
if deadlineTimer == nil {
deadlineTimer = utils.NewTimer()
}
deadlineTimer.Reset(deadline)
}
if s.dataForWriting == nil || s.canceledWrite || s.closedForShutdown {
break
}
s.mutex.Unlock()
if !notifiedSender {
s.sender.onHasStreamData(s.streamID) // must be called without holding the mutex
notifiedSender = true
}
if deadline.IsZero() {
<-s.writeChan
} else {
select {
case <-s.writeChan:
case <-time.After(time.Until(deadline)):
case <-deadlineTimer.Chan():
deadlineTimer.SetRead()
}
}
s.mutex.Lock()
}
if s.closeForShutdownErr != nil {
err = s.closeForShutdownErr
return bytesWritten, s.closeForShutdownErr
} else if s.cancelWriteErr != nil {
err = s.cancelWriteErr
return bytesWritten, s.cancelWriteErr
}
return bytesWritten, err
return bytesWritten, nil
}
// popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream
......@@ -181,12 +194,18 @@ func (s *sendStream) popStreamFrameImpl(maxBytes protocol.ByteCount) (bool /* co
return frame.FinBit, frame, s.dataForWriting != nil
}
func (s *sendStream) hasData() bool {
s.mutex.Lock()
hasData := len(s.dataForWriting) > 0
s.mutex.Unlock()
return hasData
}
func (s *sendStream) getDataForWriting(maxBytes protocol.ByteCount) ([]byte, bool /* should send FIN */) {
if s.dataForWriting == nil {
return nil, s.finishedWriting && !s.finSent
}
// TODO(#657): Flow control for the crypto stream
if s.streamID != s.version.CryptoStreamID() {
maxBytes = utils.MinByteCount(maxBytes, s.flowController.SendWindowSize())
}
......@@ -196,10 +215,12 @@ func (s *sendStream) getDataForWriting(maxBytes protocol.ByteCount) ([]byte, boo
var ret []byte
if protocol.ByteCount(len(s.dataForWriting)) > maxBytes {
ret = s.dataForWriting[:maxBytes]
ret = make([]byte, int(maxBytes))
copy(ret, s.dataForWriting[:maxBytes])
s.dataForWriting = s.dataForWriting[maxBytes:]
} else {
ret = s.dataForWriting
ret = make([]byte, len(s.dataForWriting))
copy(ret, s.dataForWriting)
s.dataForWriting = nil
s.signalWrite()
}
......@@ -210,13 +231,14 @@ func (s *sendStream) getDataForWriting(maxBytes protocol.ByteCount) ([]byte, boo
func (s *sendStream) Close() error {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.canceledWrite {
s.mutex.Unlock()
return fmt.Errorf("Close called for canceled stream %d", s.streamID)
}
s.finishedWriting = true
s.sender.onHasStreamData(s.streamID) // need to send the FIN
s.mutex.Unlock()
s.sender.onHasStreamData(s.streamID) // need to send the FIN, must be called without holding the mutex
s.ctxCancel()
return nil
}
......@@ -227,7 +249,7 @@ func (s *sendStream) CancelWrite(errorCode protocol.ApplicationErrorCode) error
s.mutex.Unlock()
if completed {
s.sender.onStreamCompleted(s.streamID)
s.sender.onStreamCompleted(s.streamID) // must be called without holding the mutex
}
return err
}
......@@ -260,12 +282,13 @@ func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) {
}
func (s *sendStream) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) {
s.flowController.UpdateSendWindow(frame.ByteOffset)
s.mutex.Lock()
if s.dataForWriting != nil {
hasStreamData := s.dataForWriting != nil
s.mutex.Unlock()
s.flowController.UpdateSendWindow(frame.ByteOffset)
if hasStreamData {
s.sender.onHasStreamData(s.streamID)
}
s.mutex.Unlock()
}
// must be called after locking the mutex
......@@ -291,12 +314,9 @@ func (s *sendStream) Context() context.Context {
func (s *sendStream) SetWriteDeadline(t time.Time) error {
s.mutex.Lock()
oldDeadline := s.writeDeadline
s.writeDeadline = t
s.deadline = t
s.mutex.Unlock()
if t.Before(oldDeadline) {
s.signalWrite()
}
s.signalWrite()
return nil
}
......
......@@ -123,6 +123,9 @@ func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (Listener,
}
func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (*server, error) {
if tlsConf == nil || (len(tlsConf.Certificates) == 0 && tlsConf.GetCertificate == nil) {
return nil, errors.New("quic: neither Certificates nor GetCertificate set in tls.Config")
}
certChain := crypto.NewCertChain(tlsConf)
kex, err := crypto.NewCurve25519KEX()
if err != nil {
......
......@@ -118,9 +118,6 @@ func (s *serverTLS) handleInitialImpl(p *receivedPacket) (quicSession, protocol.
mconf := s.mintConf.Clone()
mconf.ExtensionHandler = extHandler
// A server is allowed to perform multiple Retries.
// It doesn't make much sense, but it's something that our API allows.
// In that case it must use a source connection ID of at least 8 bytes.
connID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength)
if err != nil {
return nil, nil, err
......@@ -152,7 +149,7 @@ func (s *serverTLS) sendRetry(remoteAddr net.Addr, hdr *wire.Header) error {
if err != nil {
return err
}
connID, err := protocol.GenerateConnectionIDForInitial()
connID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength)
if err != nil {
return err
}
......
......@@ -92,12 +92,12 @@ type session struct {
sentPacketHandler ackhandler.SentPacketHandler
receivedPacketHandler ackhandler.ReceivedPacketHandler
streamFramer *streamFramer
framer *framer
windowUpdateQueue *windowUpdateQueue
connFlowController flowcontrol.ConnectionFlowController
unpacker unpacker
packer *packetPacker
packer packer
cryptoStreamHandler cryptoStreamHandler
......@@ -205,17 +205,17 @@ func newSession(
s.cryptoStreamHandler = cs
s.unpacker = newPacketUnpackerGQUIC(cs, s.version)
s.streamsMap = newStreamsMapLegacy(s.newStream, s.config.MaxIncomingStreams, s.perspective)
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPacker(
s.framer = newFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPackerLegacy(
destConnID,
srcConnID,
1,
s.sentPacketHandler.GetPacketNumberLen,
s.RemoteAddr(),
nil, // no token
divNonce,
s.cryptoStream,
cs,
s.streamFramer,
s.framer,
sentAndReceivedPacketManager{s.sentPacketHandler, s.receivedPacketHandler},
s.perspective,
s.version,
)
......@@ -226,7 +226,6 @@ func newSession(
var newClientSession = func(
conn connection,
sessionRunner sessionRunner,
hostname string,
v protocol.VersionNumber,
destConnID protocol.ConnectionID,
srcConnID protocol.ConnectionID,
......@@ -261,7 +260,6 @@ var newClientSession = func(
}
cs, err := newCryptoSetupClient(
s.cryptoStream,
hostname,
destConnID,
s.version,
tlsConf,
......@@ -278,17 +276,17 @@ var newClientSession = func(
s.cryptoStreamHandler = cs
s.unpacker = newPacketUnpackerGQUIC(cs, s.version)
s.streamsMap = newStreamsMapLegacy(s.newStream, s.config.MaxIncomingStreams, s.perspective)
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPacker(
s.framer = newFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPackerLegacy(
destConnID,
srcConnID,
1,
s.sentPacketHandler.GetPacketNumberLen,
s.RemoteAddr(),
nil, // no token
nil, // no diversification nonce
s.cryptoStream,
cs,
s.streamFramer,
s.framer,
sentAndReceivedPacketManager{s.sentPacketHandler, s.receivedPacketHandler},
s.perspective,
s.version,
)
......@@ -333,7 +331,7 @@ func newTLSServerSession(
}
s.cryptoStreamHandler = cs
s.streamsMap = newStreamsMap(s, s.newFlowController, s.config.MaxIncomingStreams, s.config.MaxIncomingUniStreams, s.perspective, s.version)
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
s.framer = newFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPacker(
s.destConnID,
s.srcConnID,
......@@ -341,9 +339,10 @@ func newTLSServerSession(
s.sentPacketHandler.GetPacketNumberLen,
s.RemoteAddr(),
nil, // no token
nil, // no diversification nonce
s.cryptoStream,
cs,
s.streamFramer,
s.framer,
sentAndReceivedPacketManager{s.sentPacketHandler, s.receivedPacketHandler},
s.perspective,
s.version,
)
......@@ -397,7 +396,7 @@ var newTLSClientSession = func(
s.cryptoStreamHandler = cs
s.unpacker = newPacketUnpacker(cs, s.version)
s.streamsMap = newStreamsMap(s, s.newFlowController, s.config.MaxIncomingStreams, s.config.MaxIncomingUniStreams, s.perspective, s.version)
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
s.framer = newFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPacker(
s.destConnID,
s.srcConnID,
......@@ -405,9 +404,10 @@ var newTLSClientSession = func(
s.sentPacketHandler.GetPacketNumberLen,
s.RemoteAddr(),
token,
nil, // no diversification nonce
s.cryptoStream,
cs,
s.streamFramer,
s.framer,
sentAndReceivedPacketManager{s.sentPacketHandler, s.receivedPacketHandler},
s.perspective,
s.version,
)
......@@ -417,6 +417,7 @@ var newTLSClientSession = func(
func (s *session) preSetup() {
s.rttStats = &congestion.RTTStats{}
s.sentPacketHandler = ackhandler.NewSentPacketHandler(s.rttStats, s.logger, s.version)
s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.rttStats, s.logger, s.version)
s.connFlowController = flowcontrol.NewConnectionFlowController(
protocol.ReceiveConnectionFlowControlWindow,
protocol.ByteCount(s.config.MaxReceiveConnectionFlowControlWindow),
......@@ -439,8 +440,7 @@ func (s *session) postSetup() error {
s.lastNetworkActivityTime = now
s.sessionCreationTime = now
s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.rttStats, s.logger, s.version)
s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.cryptoStream, s.connFlowController, s.packer.QueueControlFrame)
s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame)
return nil
}
......@@ -496,6 +496,7 @@ runLoop:
putPacketBuffer(&p.header.Raw)
case p := <-s.paramsChan:
s.processTransportParameters(&p)
continue
case _, ok := <-s.handshakeEvent:
// when the handshake is completed, the channel will be closed
s.handleHandshakeEvent(!ok)
......@@ -517,7 +518,7 @@ runLoop:
if s.config.KeepAlive && !s.keepAlivePingSent && s.handshakeComplete && time.Since(s.lastNetworkActivityTime) >= s.peerParams.IdleTimeout/2 {
// send a PING frame since there is no activity in the session
s.logger.Debugf("Sending a keep-alive ping to keep the connection alive.")
s.packer.QueueControlFrame(&wire.PingFrame{})
s.framer.QueueControlFrame(&wire.PingFrame{})
s.keepAlivePingSent = true
} else if !pacingDeadline.IsZero() && now.Before(pacingDeadline) {
// If we get to this point before the pacing deadline, we should wait until that deadline.
......@@ -527,18 +528,21 @@ runLoop:
continue
}
if err := s.sendPackets(); err != nil {
s.closeLocal(err)
}
if !s.receivedTooManyUndecrytablePacketsTime.IsZero() && s.receivedTooManyUndecrytablePacketsTime.Add(protocol.PublicResetTimeout).Before(now) && len(s.undecryptablePackets) != 0 {
s.closeLocal(qerr.Error(qerr.DecryptionFailure, "too many undecryptable packets received"))
continue
}
if !s.handshakeComplete && now.Sub(s.sessionCreationTime) >= s.config.HandshakeTimeout {
s.closeLocal(qerr.Error(qerr.HandshakeTimeout, "Crypto handshake did not complete in time."))
continue
}
if s.handshakeComplete && now.Sub(s.lastNetworkActivityTime) >= s.config.IdleTimeout {
s.closeLocal(qerr.Error(qerr.NetworkIdleTimeout, "No recent network activity."))
continue
}
if err := s.sendPackets(); err != nil {
s.closeLocal(err)
}
}
......@@ -906,12 +910,7 @@ func (s *session) handleCloseError(closeErr closeError) error {
func (s *session) processTransportParameters(params *handshake.TransportParameters) {
s.peerParams = params
s.streamsMap.UpdateLimits(params)
if params.OmitConnectionID {
s.packer.SetOmitConnectionID()
}
if params.MaxPacketSize != 0 {
s.packer.SetMaxPacketSize(params.MaxPacketSize)
}
s.packer.HandleTransportParameters(params)
s.connFlowController.UpdateSendWindow(params.ConnectionFlowControlWindow)
// the crypto stream is the only open stream at this moment
// so we don't need to update stream flow control windows
......@@ -933,6 +932,12 @@ sendLoop:
case ackhandler.SendNone:
break sendLoop
case ackhandler.SendAck:
// If we already sent packets, and the send mode switches to SendAck,
// we've just become congestion limited.
// There's no need to try to send an ACK at this moment.
if numPacketsSent > 0 {
return nil
}
// We can at most send a single ACK only packet.
// There will only be a new ACK after receiving new packets.
// SendAck is only returned when we're congestion limited, so we don't need to set the pacingt timer.
......@@ -978,21 +983,13 @@ sendLoop:
}
func (s *session) maybeSendAckOnlyPacket() error {
ack := s.receivedPacketHandler.GetAckFrame()
if ack == nil {
return nil
}
s.packer.QueueControlFrame(ack)
if s.version.UsesStopWaitingFrames() { // for gQUIC, maybe add a STOP_WAITING
if swf := s.sentPacketHandler.GetStopWaitingFrame(false); swf != nil {
s.packer.QueueControlFrame(swf)
}
}
packet, err := s.packer.PackAckPacket()
packet, err := s.packer.MaybePackAckPacket()
if err != nil {
return err
}
if packet == nil {
return nil
}
s.sentPacketHandler.SentPacket(packet.ToAckHandlerPacket())
return s.sendPackedPacket(packet)
}
......@@ -1023,9 +1020,6 @@ func (s *session) maybeSendRetransmission() (bool, error) {
s.logger.Debugf("Dequeueing retransmission for packet 0x%x", retransmitPacket.PacketNumber)
}
if s.version.UsesStopWaitingFrames() {
s.packer.QueueControlFrame(s.sentPacketHandler.GetStopWaitingFrame(true))
}
packets, err := s.packer.PackRetransmission(retransmitPacket)
if err != nil {
return false, err
......@@ -1050,9 +1044,6 @@ func (s *session) sendProbePacket() error {
}
s.logger.Debugf("Sending a retransmission for %#x as a probe packet.", p.PacketNumber)
if s.version.UsesStopWaitingFrames() {
s.packer.QueueControlFrame(s.sentPacketHandler.GetStopWaitingFrame(true))
}
packets, err := s.packer.PackRetransmission(p)
if err != nil {
return err
......@@ -1072,19 +1063,10 @@ func (s *session) sendProbePacket() error {
func (s *session) sendPacket() (bool, error) {
if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked {
s.packer.QueueControlFrame(&wire.BlockedFrame{Offset: offset})
s.framer.QueueControlFrame(&wire.BlockedFrame{Offset: offset})
}
s.windowUpdateQueue.QueueAll()
if ack := s.receivedPacketHandler.GetAckFrame(); ack != nil {
s.packer.QueueControlFrame(ack)
if s.version.UsesStopWaitingFrames() {
if swf := s.sentPacketHandler.GetStopWaitingFrame(false); swf != nil {
s.packer.QueueControlFrame(swf)
}
}
}
packet, err := s.packer.PackPacket()
if err != nil || packet == nil {
return false, err
......@@ -1245,7 +1227,7 @@ func (s *session) tryDecryptingQueuedPackets() {
}
func (s *session) queueControlFrame(f wire.Frame) {
s.packer.QueueControlFrame(f)
s.framer.QueueControlFrame(f)
s.scheduleSending()
}
......@@ -1260,7 +1242,9 @@ func (s *session) onHasConnectionWindowUpdate() {
}
func (s *session) onHasStreamData(id protocol.StreamID) {
s.streamFramer.AddActiveStream(id)
if id != s.version.CryptoStreamID() {
s.framer.AddActiveStream(id)
}
s.scheduleSending()
}
......
......@@ -52,6 +52,7 @@ type streamI interface {
handleRstStreamFrame(*wire.RstStreamFrame) error
getWindowUpdate() protocol.ByteCount
// for sending
hasData() bool
handleStopSendingFrame(*wire.StopSendingFrame)
popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool)
handleMaxStreamDataFrame(*wire.MaxStreamDataFrame)
......
......@@ -14,7 +14,6 @@ type windowUpdateQueue struct {
queue map[protocol.StreamID]bool // used as a set
queuedConn bool // connection-level window update
cryptoStream cryptoStream
streamGetter streamGetter
connFlowController flowcontrol.ConnectionFlowController
callback func(wire.Frame)
......@@ -22,14 +21,12 @@ type windowUpdateQueue struct {
func newWindowUpdateQueue(
streamGetter streamGetter,
cryptoStream cryptoStream,
connFC flowcontrol.ConnectionFlowController,
cb func(wire.Frame),
) *windowUpdateQueue {
return &windowUpdateQueue{
queue: make(map[protocol.StreamID]bool),
streamGetter: streamGetter,
cryptoStream: cryptoStream,
connFlowController: connFC,
callback: cb,
}
......@@ -55,17 +52,12 @@ func (q *windowUpdateQueue) QueueAll() {
q.queuedConn = false
}
// queue all stream-level window updates
var offset protocol.ByteCount
for id := range q.queue {
if id == q.cryptoStream.StreamID() {
offset = q.cryptoStream.getWindowUpdate()
} else {
str, err := q.streamGetter.GetOrOpenReceiveStream(id)
if err != nil || str == nil { // the stream can be nil if it was completed before dequeing the window update
continue
}
offset = str.getWindowUpdate()
str, err := q.streamGetter.GetOrOpenReceiveStream(id)
if err != nil || str == nil { // the stream can be nil if it was completed before dequeing the window update
continue
}
offset := str.getWindowUpdate()
if offset == 0 { // can happen if we received a final offset, right after queueing the window update
continue
}
......
......@@ -130,7 +130,7 @@
"importpath": "github.com/lucas-clemente/quic-go",
"repository": "https://github.com/lucas-clemente/quic-go",
"vcs": "git",
"revision": "71635f6961ad00ca5c088be625624e4a2cd1c066",
"revision": "f90751eabaa39364e3861ee5a8b179f140847d7e",
"branch": "HEAD",
"notests": true
},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment