mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 20:57:36 +03:00
132 lines
4.3 KiB
Go
132 lines
4.3 KiB
Go
package handshake
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/lucas-clemente/quic-go/qerr"
|
|
|
|
"github.com/bifurcation/mint"
|
|
"github.com/bifurcation/mint/syntax"
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
)
|
|
|
|
type extensionHandlerClient struct {
|
|
ourParams *TransportParameters
|
|
paramsChan chan TransportParameters
|
|
|
|
initialVersion protocol.VersionNumber
|
|
supportedVersions []protocol.VersionNumber
|
|
version protocol.VersionNumber
|
|
}
|
|
|
|
var _ mint.AppExtensionHandler = &extensionHandlerClient{}
|
|
var _ TLSExtensionHandler = &extensionHandlerClient{}
|
|
|
|
// NewExtensionHandlerClient creates a new extension handler for the client.
|
|
func NewExtensionHandlerClient(
|
|
params *TransportParameters,
|
|
initialVersion protocol.VersionNumber,
|
|
supportedVersions []protocol.VersionNumber,
|
|
version protocol.VersionNumber,
|
|
) TLSExtensionHandler {
|
|
// The client reads the transport parameters from the Encrypted Extensions message.
|
|
// The paramsChan is used in the session's run loop's select statement.
|
|
// We have to use an unbuffered channel here to make sure that the session actually processes the transport parameters immediately.
|
|
paramsChan := make(chan TransportParameters)
|
|
return &extensionHandlerClient{
|
|
ourParams: params,
|
|
paramsChan: paramsChan,
|
|
initialVersion: initialVersion,
|
|
supportedVersions: supportedVersions,
|
|
version: version,
|
|
}
|
|
}
|
|
|
|
func (h *extensionHandlerClient) Send(hType mint.HandshakeType, el *mint.ExtensionList) error {
|
|
if hType != mint.HandshakeTypeClientHello {
|
|
return nil
|
|
}
|
|
|
|
data, err := syntax.Marshal(clientHelloTransportParameters{
|
|
InitialVersion: uint32(h.initialVersion),
|
|
Parameters: h.ourParams.getTransportParameters(),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return el.Add(&tlsExtensionBody{data})
|
|
}
|
|
|
|
func (h *extensionHandlerClient) Receive(hType mint.HandshakeType, el *mint.ExtensionList) error {
|
|
ext := &tlsExtensionBody{}
|
|
found, err := el.Find(ext)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if hType != mint.HandshakeTypeEncryptedExtensions {
|
|
if found {
|
|
return fmt.Errorf("Unexpected QUIC extension in handshake message %d", hType)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// hType == mint.HandshakeTypeEncryptedExtensions
|
|
if !found {
|
|
return errors.New("EncryptedExtensions message didn't contain a QUIC extension")
|
|
}
|
|
|
|
eetp := &encryptedExtensionsTransportParameters{}
|
|
if _, err := syntax.Unmarshal(ext.data, eetp); err != nil {
|
|
return err
|
|
}
|
|
serverSupportedVersions := make([]protocol.VersionNumber, len(eetp.SupportedVersions))
|
|
for i, v := range eetp.SupportedVersions {
|
|
serverSupportedVersions[i] = protocol.VersionNumber(v)
|
|
}
|
|
// check that the negotiated_version is the current version
|
|
if protocol.VersionNumber(eetp.NegotiatedVersion) != h.version {
|
|
return qerr.Error(qerr.VersionNegotiationMismatch, "current version doesn't match negotiated_version")
|
|
}
|
|
// check that the current version is included in the supported versions
|
|
if !protocol.IsSupportedVersion(serverSupportedVersions, h.version) {
|
|
return qerr.Error(qerr.VersionNegotiationMismatch, "current version not included in the supported versions")
|
|
}
|
|
// if version negotiation was performed, check that we would have selected the current version based on the supported versions sent by the server
|
|
if h.version != h.initialVersion {
|
|
negotiatedVersion, ok := protocol.ChooseSupportedVersion(h.supportedVersions, serverSupportedVersions)
|
|
if !ok || h.version != negotiatedVersion {
|
|
return qerr.Error(qerr.VersionNegotiationMismatch, "would have picked a different version")
|
|
}
|
|
}
|
|
|
|
// check that the server sent the stateless reset token
|
|
var foundStatelessResetToken bool
|
|
for _, p := range eetp.Parameters {
|
|
if p.Parameter == statelessResetTokenParameterID {
|
|
if len(p.Value) != 16 {
|
|
return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", len(p.Value))
|
|
}
|
|
foundStatelessResetToken = true
|
|
// TODO: handle this value
|
|
}
|
|
}
|
|
if !foundStatelessResetToken {
|
|
// TODO: return the right error here
|
|
return errors.New("server didn't sent stateless_reset_token")
|
|
}
|
|
params, err := readTransportParamters(eetp.Parameters)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// TODO(#878): remove this when implementing the MAX_STREAM_ID frame
|
|
params.MaxStreams = math.MaxUint32
|
|
h.paramsChan <- *params
|
|
return nil
|
|
}
|
|
|
|
func (h *extensionHandlerClient) GetPeerParams() <-chan TransportParameters {
|
|
return h.paramsChan
|
|
}
|