265 lines
10 KiB
Go
265 lines
10 KiB
Go
package handshake
|
|
|
|
import (
|
|
"bytes"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/lucas-clemente/quic-go/protocol"
|
|
"github.com/lucas-clemente/quic-go/qerr"
|
|
"github.com/lucas-clemente/quic-go/utils"
|
|
)
|
|
|
|
// ConnectionParametersManager negotiates and stores the connection parameters
|
|
// A ConnectionParametersManager can be used for a server as well as a client
|
|
// For the server:
|
|
// 1. call SetFromMap with the values received in the CHLO. This sets the corresponding values here, subject to negotiation
|
|
// 2. call GetHelloMap to get the values to send in the SHLO
|
|
// For the client:
|
|
// 1. call GetHelloMap to get the values to send in a CHLO
|
|
// 2. call SetFromMap with the values received in the SHLO
|
|
type ConnectionParametersManager interface {
|
|
SetFromMap(map[Tag][]byte) error
|
|
GetHelloMap() (map[Tag][]byte, error)
|
|
|
|
GetSendStreamFlowControlWindow() protocol.ByteCount
|
|
GetSendConnectionFlowControlWindow() protocol.ByteCount
|
|
GetReceiveStreamFlowControlWindow() protocol.ByteCount
|
|
GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount
|
|
GetReceiveConnectionFlowControlWindow() protocol.ByteCount
|
|
GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount
|
|
GetMaxOutgoingStreams() uint32
|
|
GetMaxIncomingStreams() uint32
|
|
GetIdleConnectionStateLifetime() time.Duration
|
|
TruncateConnectionID() bool
|
|
}
|
|
|
|
type connectionParametersManager struct {
|
|
mutex sync.RWMutex
|
|
|
|
version protocol.VersionNumber
|
|
perspective protocol.Perspective
|
|
|
|
flowControlNegotiated bool
|
|
|
|
truncateConnectionID bool
|
|
maxStreamsPerConnection uint32
|
|
maxIncomingDynamicStreamsPerConnection uint32
|
|
idleConnectionStateLifetime time.Duration
|
|
sendStreamFlowControlWindow protocol.ByteCount
|
|
sendConnectionFlowControlWindow protocol.ByteCount
|
|
receiveStreamFlowControlWindow protocol.ByteCount
|
|
receiveConnectionFlowControlWindow protocol.ByteCount
|
|
}
|
|
|
|
var _ ConnectionParametersManager = &connectionParametersManager{}
|
|
|
|
// ErrMalformedTag is returned when the tag value cannot be read
|
|
var (
|
|
ErrMalformedTag = qerr.Error(qerr.InvalidCryptoMessageParameter, "malformed Tag value")
|
|
ErrFlowControlRenegotiationNotSupported = qerr.Error(qerr.InvalidCryptoMessageParameter, "renegotiation of flow control parameters not supported")
|
|
)
|
|
|
|
// NewConnectionParamatersManager creates a new connection parameters manager
|
|
func NewConnectionParamatersManager(pers protocol.Perspective, v protocol.VersionNumber) ConnectionParametersManager {
|
|
h := &connectionParametersManager{
|
|
perspective: pers,
|
|
version: v,
|
|
sendStreamFlowControlWindow: protocol.InitialStreamFlowControlWindow, // can only be changed by the client
|
|
sendConnectionFlowControlWindow: protocol.InitialConnectionFlowControlWindow, // can only be changed by the client
|
|
receiveStreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
|
|
receiveConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
|
|
}
|
|
|
|
if h.perspective == protocol.PerspectiveServer {
|
|
h.idleConnectionStateLifetime = protocol.DefaultIdleTimeout
|
|
h.maxStreamsPerConnection = protocol.MaxStreamsPerConnection // this is the value negotiated based on what the client sent
|
|
h.maxIncomingDynamicStreamsPerConnection = protocol.MaxStreamsPerConnection // "incoming" seen from the client's perspective
|
|
} else {
|
|
h.idleConnectionStateLifetime = protocol.MaxIdleTimeoutClient
|
|
h.maxStreamsPerConnection = protocol.MaxStreamsPerConnection // this is the value negotiated based on what the client sent
|
|
h.maxIncomingDynamicStreamsPerConnection = protocol.MaxStreamsPerConnection // "incoming" seen from the server's perspective
|
|
}
|
|
|
|
return h
|
|
}
|
|
|
|
// SetFromMap reads all params
|
|
func (h *connectionParametersManager) SetFromMap(params map[Tag][]byte) error {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if value, ok := params[TagTCID]; ok && h.perspective == protocol.PerspectiveServer {
|
|
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
|
if err != nil {
|
|
return ErrMalformedTag
|
|
}
|
|
h.truncateConnectionID = (clientValue == 0)
|
|
}
|
|
if value, ok := params[TagMSPC]; ok {
|
|
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
|
if err != nil {
|
|
return ErrMalformedTag
|
|
}
|
|
h.maxStreamsPerConnection = h.negotiateMaxStreamsPerConnection(clientValue)
|
|
}
|
|
if value, ok := params[TagMIDS]; ok {
|
|
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
|
if err != nil {
|
|
return ErrMalformedTag
|
|
}
|
|
h.maxIncomingDynamicStreamsPerConnection = h.negotiateMaxIncomingDynamicStreamsPerConnection(clientValue)
|
|
}
|
|
if value, ok := params[TagICSL]; ok {
|
|
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
|
|
if err != nil {
|
|
return ErrMalformedTag
|
|
}
|
|
h.idleConnectionStateLifetime = h.negotiateIdleConnectionStateLifetime(time.Duration(clientValue) * time.Second)
|
|
}
|
|
if value, ok := params[TagSFCW]; ok {
|
|
if h.flowControlNegotiated {
|
|
return ErrFlowControlRenegotiationNotSupported
|
|
}
|
|
sendStreamFlowControlWindow, err := utils.ReadUint32(bytes.NewBuffer(value))
|
|
if err != nil {
|
|
return ErrMalformedTag
|
|
}
|
|
h.sendStreamFlowControlWindow = protocol.ByteCount(sendStreamFlowControlWindow)
|
|
}
|
|
if value, ok := params[TagCFCW]; ok {
|
|
if h.flowControlNegotiated {
|
|
return ErrFlowControlRenegotiationNotSupported
|
|
}
|
|
sendConnectionFlowControlWindow, err := utils.ReadUint32(bytes.NewBuffer(value))
|
|
if err != nil {
|
|
return ErrMalformedTag
|
|
}
|
|
h.sendConnectionFlowControlWindow = protocol.ByteCount(sendConnectionFlowControlWindow)
|
|
}
|
|
|
|
_, containsSFCW := params[TagSFCW]
|
|
_, containsCFCW := params[TagCFCW]
|
|
if containsCFCW || containsSFCW {
|
|
h.flowControlNegotiated = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *connectionParametersManager) negotiateMaxStreamsPerConnection(clientValue uint32) uint32 {
|
|
return utils.MinUint32(clientValue, protocol.MaxStreamsPerConnection)
|
|
}
|
|
|
|
func (h *connectionParametersManager) negotiateMaxIncomingDynamicStreamsPerConnection(clientValue uint32) uint32 {
|
|
return utils.MinUint32(clientValue, protocol.MaxIncomingDynamicStreamsPerConnection)
|
|
}
|
|
|
|
func (h *connectionParametersManager) negotiateIdleConnectionStateLifetime(clientValue time.Duration) time.Duration {
|
|
if h.perspective == protocol.PerspectiveServer {
|
|
return utils.MinDuration(clientValue, protocol.MaxIdleTimeoutServer)
|
|
}
|
|
return utils.MinDuration(clientValue, protocol.MaxIdleTimeoutClient)
|
|
}
|
|
|
|
// GetHelloMap gets all parameters needed for the Hello message
|
|
func (h *connectionParametersManager) GetHelloMap() (map[Tag][]byte, error) {
|
|
sfcw := bytes.NewBuffer([]byte{})
|
|
utils.WriteUint32(sfcw, uint32(h.GetReceiveStreamFlowControlWindow()))
|
|
cfcw := bytes.NewBuffer([]byte{})
|
|
utils.WriteUint32(cfcw, uint32(h.GetReceiveConnectionFlowControlWindow()))
|
|
mspc := bytes.NewBuffer([]byte{})
|
|
utils.WriteUint32(mspc, h.maxStreamsPerConnection)
|
|
mids := bytes.NewBuffer([]byte{})
|
|
utils.WriteUint32(mids, protocol.MaxIncomingDynamicStreamsPerConnection)
|
|
icsl := bytes.NewBuffer([]byte{})
|
|
utils.WriteUint32(icsl, uint32(h.GetIdleConnectionStateLifetime()/time.Second))
|
|
|
|
return map[Tag][]byte{
|
|
TagICSL: icsl.Bytes(),
|
|
TagMSPC: mspc.Bytes(),
|
|
TagMIDS: mids.Bytes(),
|
|
TagCFCW: cfcw.Bytes(),
|
|
TagSFCW: sfcw.Bytes(),
|
|
}, nil
|
|
}
|
|
|
|
// GetSendStreamFlowControlWindow gets the size of the stream-level flow control window for sending data
|
|
func (h *connectionParametersManager) GetSendStreamFlowControlWindow() protocol.ByteCount {
|
|
h.mutex.RLock()
|
|
defer h.mutex.RUnlock()
|
|
return h.sendStreamFlowControlWindow
|
|
}
|
|
|
|
// GetSendConnectionFlowControlWindow gets the size of the stream-level flow control window for sending data
|
|
func (h *connectionParametersManager) GetSendConnectionFlowControlWindow() protocol.ByteCount {
|
|
h.mutex.RLock()
|
|
defer h.mutex.RUnlock()
|
|
return h.sendConnectionFlowControlWindow
|
|
}
|
|
|
|
// GetReceiveStreamFlowControlWindow gets the size of the stream-level flow control window for receiving data
|
|
func (h *connectionParametersManager) GetReceiveStreamFlowControlWindow() protocol.ByteCount {
|
|
h.mutex.RLock()
|
|
defer h.mutex.RUnlock()
|
|
return h.receiveStreamFlowControlWindow
|
|
}
|
|
|
|
// GetMaxReceiveStreamFlowControlWindow gets the maximum size of the stream-level flow control window for sending data
|
|
func (h *connectionParametersManager) GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount {
|
|
if h.perspective == protocol.PerspectiveServer {
|
|
return protocol.MaxReceiveStreamFlowControlWindowServer
|
|
}
|
|
return protocol.MaxReceiveStreamFlowControlWindowClient
|
|
}
|
|
|
|
// GetReceiveConnectionFlowControlWindow gets the size of the stream-level flow control window for receiving data
|
|
func (h *connectionParametersManager) GetReceiveConnectionFlowControlWindow() protocol.ByteCount {
|
|
h.mutex.RLock()
|
|
defer h.mutex.RUnlock()
|
|
return h.receiveConnectionFlowControlWindow
|
|
}
|
|
|
|
// GetMaxReceiveConnectionFlowControlWindow gets the maximum size of the stream-level flow control window for sending data
|
|
func (h *connectionParametersManager) GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount {
|
|
if h.perspective == protocol.PerspectiveServer {
|
|
return protocol.MaxReceiveConnectionFlowControlWindowServer
|
|
}
|
|
return protocol.MaxReceiveConnectionFlowControlWindowClient
|
|
}
|
|
|
|
// GetMaxOutgoingStreams gets the maximum number of outgoing streams per connection
|
|
func (h *connectionParametersManager) GetMaxOutgoingStreams() uint32 {
|
|
h.mutex.RLock()
|
|
defer h.mutex.RUnlock()
|
|
|
|
return h.maxIncomingDynamicStreamsPerConnection
|
|
}
|
|
|
|
// GetMaxIncomingStreams get the maximum number of incoming streams per connection
|
|
func (h *connectionParametersManager) GetMaxIncomingStreams() uint32 {
|
|
h.mutex.RLock()
|
|
defer h.mutex.RUnlock()
|
|
|
|
maxStreams := protocol.MaxIncomingDynamicStreamsPerConnection
|
|
return utils.MaxUint32(uint32(maxStreams)+protocol.MaxStreamsMinimumIncrement, uint32(float64(maxStreams)*protocol.MaxStreamsMultiplier))
|
|
}
|
|
|
|
// GetIdleConnectionStateLifetime gets the idle timeout
|
|
func (h *connectionParametersManager) GetIdleConnectionStateLifetime() time.Duration {
|
|
h.mutex.RLock()
|
|
defer h.mutex.RUnlock()
|
|
return h.idleConnectionStateLifetime
|
|
}
|
|
|
|
// TruncateConnectionID determines if the client requests truncated ConnectionIDs
|
|
func (h *connectionParametersManager) TruncateConnectionID() bool {
|
|
if h.perspective == protocol.PerspectiveClient {
|
|
return false
|
|
}
|
|
|
|
h.mutex.RLock()
|
|
defer h.mutex.RUnlock()
|
|
return h.truncateConnectionID
|
|
}
|