implement (un)marshalling of the transport parameters

This commit is contained in:
Marten Seemann
2018-08-19 12:11:56 +07:00
parent 00e1884a88
commit e0e831f1fc
8 changed files with 356 additions and 277 deletions

View File

@@ -96,98 +96,110 @@ func (p *TransportParameters) getHelloMap() map[Tag][]byte {
return tags
}
// readTransportParameters reads the transport parameters sent in the QUIC TLS extension
func readTransportParameters(paramsList []transportParameter) (*TransportParameters, error) {
params := &TransportParameters{}
func (p *TransportParameters) unmarshal(data []byte) error {
var foundIdleTimeout bool
for _, p := range paramsList {
switch p.Parameter {
for len(data) >= 4 {
paramID := 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) {
case initialMaxStreamDataParameterID:
if len(p.Value) != 4 {
return nil, fmt.Errorf("wrong length for initial_max_stream_data: %d (expected 4)", len(p.Value))
if paramLen != 4 {
return fmt.Errorf("wrong length for initial_max_stream_data: %d (expected 4)", paramLen)
}
params.StreamFlowControlWindow = protocol.ByteCount(binary.BigEndian.Uint32(p.Value))
p.StreamFlowControlWindow = protocol.ByteCount(binary.BigEndian.Uint32(data[:4]))
case initialMaxDataParameterID:
if len(p.Value) != 4 {
return nil, fmt.Errorf("wrong length for initial_max_data: %d (expected 4)", len(p.Value))
if paramLen != 4 {
return fmt.Errorf("wrong length for initial_max_data: %d (expected 4)", paramLen)
}
params.ConnectionFlowControlWindow = protocol.ByteCount(binary.BigEndian.Uint32(p.Value))
p.ConnectionFlowControlWindow = protocol.ByteCount(binary.BigEndian.Uint32(data[:4]))
case initialMaxBidiStreamsParameterID:
if len(p.Value) != 2 {
return nil, fmt.Errorf("wrong length for initial_max_stream_id_bidi: %d (expected 2)", len(p.Value))
if paramLen != 2 {
return fmt.Errorf("wrong length for initial_max_stream_id_bidi: %d (expected 2)", paramLen)
}
params.MaxBidiStreams = binary.BigEndian.Uint16(p.Value)
p.MaxBidiStreams = binary.BigEndian.Uint16(data[:2])
case initialMaxUniStreamsParameterID:
if len(p.Value) != 2 {
return nil, fmt.Errorf("wrong length for initial_max_stream_id_uni: %d (expected 2)", len(p.Value))
if paramLen != 2 {
return fmt.Errorf("wrong length for initial_max_stream_id_uni: %d (expected 2)", paramLen)
}
params.MaxUniStreams = binary.BigEndian.Uint16(p.Value)
p.MaxUniStreams = binary.BigEndian.Uint16(data[:2])
case idleTimeoutParameterID:
foundIdleTimeout = true
if len(p.Value) != 2 {
return nil, fmt.Errorf("wrong length for idle_timeout: %d (expected 2)", len(p.Value))
if paramLen != 2 {
return fmt.Errorf("wrong length for idle_timeout: %d (expected 2)", paramLen)
}
params.IdleTimeout = utils.MaxDuration(protocol.MinRemoteIdleTimeout, time.Duration(binary.BigEndian.Uint16(p.Value))*time.Second)
p.IdleTimeout = utils.MaxDuration(protocol.MinRemoteIdleTimeout, time.Duration(binary.BigEndian.Uint16(data[:2]))*time.Second)
case maxPacketSizeParameterID:
if len(p.Value) != 2 {
return nil, fmt.Errorf("wrong length for max_packet_size: %d (expected 2)", len(p.Value))
if paramLen != 2 {
return fmt.Errorf("wrong length for max_packet_size: %d (expected 2)", paramLen)
}
maxPacketSize := protocol.ByteCount(binary.BigEndian.Uint16(p.Value))
maxPacketSize := protocol.ByteCount(binary.BigEndian.Uint16(data[:2]))
if maxPacketSize < 1200 {
return nil, fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", maxPacketSize)
return fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", maxPacketSize)
}
params.MaxPacketSize = maxPacketSize
p.MaxPacketSize = maxPacketSize
case disableMigrationParameterID:
if len(p.Value) != 0 {
return nil, fmt.Errorf("wrong length for disable_migration: %d (expected empty)", len(p.Value))
if paramLen != 0 {
return fmt.Errorf("wrong length for disable_migration: %d (expected empty)", paramLen)
}
params.DisableMigration = true
p.DisableMigration = true
case statelessResetTokenParameterID:
if len(p.Value) != 16 {
return nil, fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", len(p.Value))
if paramLen != 16 {
return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen)
}
params.StatelessResetToken = p.Value
p.StatelessResetToken = data[:16]
}
data = data[paramLen:]
}
if !foundIdleTimeout {
return nil, errors.New("missing parameter")
if len(data) != 0 {
return fmt.Errorf("should have read all data. Still have %d bytes", len(data))
}
return params, nil
if !foundIdleTimeout {
return errors.New("missing parameter")
}
return nil
}
// GetTransportParameters gets the parameters needed for the TLS handshake.
// It doesn't send the initial_max_stream_id_uni parameter, so the peer isn't allowed to open any unidirectional streams.
func (p *TransportParameters) getTransportParameters() []transportParameter {
initialMaxStreamData := make([]byte, 4)
binary.BigEndian.PutUint32(initialMaxStreamData, uint32(p.StreamFlowControlWindow))
initialMaxData := make([]byte, 4)
binary.BigEndian.PutUint32(initialMaxData, uint32(p.ConnectionFlowControlWindow))
initialMaxBidiStreamID := make([]byte, 2)
binary.BigEndian.PutUint16(initialMaxBidiStreamID, p.MaxBidiStreams)
initialMaxUniStreamID := make([]byte, 2)
binary.BigEndian.PutUint16(initialMaxUniStreamID, p.MaxUniStreams)
idleTimeout := make([]byte, 2)
binary.BigEndian.PutUint16(idleTimeout, uint16(p.IdleTimeout/time.Second))
maxPacketSize := make([]byte, 2)
binary.BigEndian.PutUint16(maxPacketSize, uint16(protocol.MaxReceivePacketSize))
params := []transportParameter{
{initialMaxStreamDataParameterID, initialMaxStreamData},
{initialMaxDataParameterID, initialMaxData},
{initialMaxBidiStreamsParameterID, initialMaxBidiStreamID},
{initialMaxUniStreamsParameterID, initialMaxUniStreamID},
{idleTimeoutParameterID, idleTimeout},
{maxPacketSizeParameterID, maxPacketSize},
}
func (p *TransportParameters) marshal(b *bytes.Buffer) {
// initial_max_stream_data
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataParameterID))
utils.BigEndian.WriteUint16(b, 4)
utils.BigEndian.WriteUint32(b, uint32(p.StreamFlowControlWindow))
// initial_max_data
utils.BigEndian.WriteUint16(b, uint16(initialMaxDataParameterID))
utils.BigEndian.WriteUint16(b, 4)
utils.BigEndian.WriteUint32(b, uint32(p.ConnectionFlowControlWindow))
// initial_max_bidi_streams
utils.BigEndian.WriteUint16(b, uint16(initialMaxBidiStreamsParameterID))
utils.BigEndian.WriteUint16(b, 2)
utils.BigEndian.WriteUint16(b, p.MaxBidiStreams)
// initial_max_uni_streams
utils.BigEndian.WriteUint16(b, uint16(initialMaxUniStreamsParameterID))
utils.BigEndian.WriteUint16(b, 2)
utils.BigEndian.WriteUint16(b, p.MaxUniStreams)
// idle_timeout
utils.BigEndian.WriteUint16(b, uint16(idleTimeoutParameterID))
utils.BigEndian.WriteUint16(b, 2)
utils.BigEndian.WriteUint16(b, uint16(p.IdleTimeout/time.Second))
// max_packet_size
utils.BigEndian.WriteUint16(b, uint16(maxPacketSizeParameterID))
utils.BigEndian.WriteUint16(b, 2)
utils.BigEndian.WriteUint16(b, uint16(protocol.MaxReceivePacketSize))
// disable_migration
if p.DisableMigration {
params = append(params, transportParameter{disableMigrationParameterID, nil})
utils.BigEndian.WriteUint16(b, uint16(disableMigrationParameterID))
utils.BigEndian.WriteUint16(b, 0)
}
if len(p.StatelessResetToken) > 0 {
params = append(params, transportParameter{statelessResetTokenParameterID, p.StatelessResetToken})
utils.BigEndian.WriteUint16(b, uint16(statelessResetTokenParameterID))
utils.BigEndian.WriteUint16(b, uint16(len(p.StatelessResetToken))) // should always be 16 bytes
b.Write(p.StatelessResetToken)
}
return params
}
// String returns a string representation, intended for logging.