mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-10-31 00:08:08 -04:00
211 lines
8.3 KiB
Go
211 lines
8.3 KiB
Go
package handshake
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"time"
|
|
|
|
"v2ray.com/core/external/github.com/lucas-clemente/quic-go/internal/protocol"
|
|
"v2ray.com/core/external/github.com/lucas-clemente/quic-go/internal/utils"
|
|
)
|
|
|
|
type transportParameterID uint16
|
|
|
|
const (
|
|
originalConnectionIDParameterID transportParameterID = 0x0
|
|
idleTimeoutParameterID transportParameterID = 0x1
|
|
statelessResetTokenParameterID transportParameterID = 0x2
|
|
maxPacketSizeParameterID transportParameterID = 0x3
|
|
initialMaxDataParameterID transportParameterID = 0x4
|
|
initialMaxStreamDataBidiLocalParameterID transportParameterID = 0x5
|
|
initialMaxStreamDataBidiRemoteParameterID transportParameterID = 0x6
|
|
initialMaxStreamDataUniParameterID transportParameterID = 0x7
|
|
initialMaxStreamsBidiParameterID transportParameterID = 0x8
|
|
initialMaxStreamsUniParameterID transportParameterID = 0x9
|
|
disableMigrationParameterID transportParameterID = 0xc
|
|
)
|
|
|
|
// TransportParameters are parameters sent to the peer during the handshake
|
|
type TransportParameters struct {
|
|
InitialMaxStreamDataBidiLocal protocol.ByteCount
|
|
InitialMaxStreamDataBidiRemote protocol.ByteCount
|
|
InitialMaxStreamDataUni protocol.ByteCount
|
|
InitialMaxData protocol.ByteCount
|
|
|
|
MaxPacketSize protocol.ByteCount
|
|
|
|
MaxUniStreams uint64
|
|
MaxBidiStreams uint64
|
|
|
|
IdleTimeout time.Duration
|
|
DisableMigration bool
|
|
|
|
StatelessResetToken []byte
|
|
OriginalConnectionID protocol.ConnectionID
|
|
}
|
|
|
|
func (p *TransportParameters) unmarshal(data []byte, sentBy protocol.Perspective) error {
|
|
// needed to check that every parameter is only sent at most once
|
|
var parameterIDs []transportParameterID
|
|
|
|
r := bytes.NewReader(data)
|
|
for r.Len() >= 4 {
|
|
paramIDInt, _ := utils.BigEndian.ReadUint16(r)
|
|
paramID := transportParameterID(paramIDInt)
|
|
paramLen, _ := utils.BigEndian.ReadUint16(r)
|
|
parameterIDs = append(parameterIDs, paramID)
|
|
switch paramID {
|
|
case initialMaxStreamDataBidiLocalParameterID,
|
|
initialMaxStreamDataBidiRemoteParameterID,
|
|
initialMaxStreamDataUniParameterID,
|
|
initialMaxDataParameterID,
|
|
initialMaxStreamsBidiParameterID,
|
|
initialMaxStreamsUniParameterID,
|
|
idleTimeoutParameterID,
|
|
maxPacketSizeParameterID:
|
|
if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
if r.Len() < int(paramLen) {
|
|
return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", r.Len(), paramLen)
|
|
}
|
|
switch paramID {
|
|
case disableMigrationParameterID:
|
|
if paramLen != 0 {
|
|
return fmt.Errorf("wrong length for disable_migration: %d (expected empty)", paramLen)
|
|
}
|
|
p.DisableMigration = true
|
|
case statelessResetTokenParameterID:
|
|
if sentBy == protocol.PerspectiveClient {
|
|
return errors.New("client sent a stateless_reset_token")
|
|
}
|
|
if paramLen != 16 {
|
|
return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen)
|
|
}
|
|
b := make([]byte, 16)
|
|
r.Read(b)
|
|
p.StatelessResetToken = b
|
|
case originalConnectionIDParameterID:
|
|
if sentBy == protocol.PerspectiveClient {
|
|
return errors.New("client sent an original_connection_id")
|
|
}
|
|
p.OriginalConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen))
|
|
default:
|
|
r.Seek(int64(paramLen), io.SeekCurrent)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 r.Len() != 0 {
|
|
return fmt.Errorf("should have read all data. Still have %d bytes", r.Len())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *TransportParameters) readNumericTransportParameter(
|
|
r *bytes.Reader,
|
|
paramID transportParameterID,
|
|
expectedLen int,
|
|
) error {
|
|
remainingLen := r.Len()
|
|
val, err := utils.ReadVarInt(r)
|
|
if err != nil {
|
|
return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err)
|
|
}
|
|
if remainingLen-r.Len() != expectedLen {
|
|
return fmt.Errorf("inconsistent transport parameter length for %d", paramID)
|
|
}
|
|
switch paramID {
|
|
case initialMaxStreamDataBidiLocalParameterID:
|
|
p.InitialMaxStreamDataBidiLocal = protocol.ByteCount(val)
|
|
case initialMaxStreamDataBidiRemoteParameterID:
|
|
p.InitialMaxStreamDataBidiRemote = protocol.ByteCount(val)
|
|
case initialMaxStreamDataUniParameterID:
|
|
p.InitialMaxStreamDataUni = protocol.ByteCount(val)
|
|
case initialMaxDataParameterID:
|
|
p.InitialMaxData = protocol.ByteCount(val)
|
|
case initialMaxStreamsBidiParameterID:
|
|
p.MaxBidiStreams = val
|
|
case initialMaxStreamsUniParameterID:
|
|
p.MaxUniStreams = val
|
|
case idleTimeoutParameterID:
|
|
p.IdleTimeout = utils.MaxDuration(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Second)
|
|
case maxPacketSizeParameterID:
|
|
if val < 1200 {
|
|
return fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", val)
|
|
}
|
|
p.MaxPacketSize = protocol.ByteCount(val)
|
|
default:
|
|
return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *TransportParameters) marshal(b *bytes.Buffer) {
|
|
// initial_max_stream_data_bidi_local
|
|
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataBidiLocalParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataBidiLocal))))
|
|
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataBidiLocal))
|
|
// initial_max_stream_data_bidi_remote
|
|
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataBidiRemoteParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataBidiRemote))))
|
|
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataBidiRemote))
|
|
// initial_max_stream_data_uni
|
|
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataUniParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataUni))))
|
|
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataUni))
|
|
// initial_max_data
|
|
utils.BigEndian.WriteUint16(b, uint16(initialMaxDataParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxData))))
|
|
utils.WriteVarInt(b, uint64(p.InitialMaxData))
|
|
// initial_max_bidi_streams
|
|
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamsBidiParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(p.MaxBidiStreams)))
|
|
utils.WriteVarInt(b, p.MaxBidiStreams)
|
|
// initial_max_uni_streams
|
|
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamsUniParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(p.MaxUniStreams)))
|
|
utils.WriteVarInt(b, p.MaxUniStreams)
|
|
// idle_timeout
|
|
utils.BigEndian.WriteUint16(b, uint16(idleTimeoutParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.IdleTimeout/time.Second))))
|
|
utils.WriteVarInt(b, uint64(p.IdleTimeout/time.Second))
|
|
// max_packet_size
|
|
utils.BigEndian.WriteUint16(b, uint16(maxPacketSizeParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(protocol.MaxReceivePacketSize))))
|
|
utils.WriteVarInt(b, uint64(protocol.MaxReceivePacketSize))
|
|
// disable_migration
|
|
if p.DisableMigration {
|
|
utils.BigEndian.WriteUint16(b, uint16(disableMigrationParameterID))
|
|
utils.BigEndian.WriteUint16(b, 0)
|
|
}
|
|
if len(p.StatelessResetToken) > 0 {
|
|
utils.BigEndian.WriteUint16(b, uint16(statelessResetTokenParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(len(p.StatelessResetToken))) // should always be 16 bytes
|
|
b.Write(p.StatelessResetToken)
|
|
}
|
|
// original_connection_id
|
|
if p.OriginalConnectionID.Len() > 0 {
|
|
utils.BigEndian.WriteUint16(b, uint16(originalConnectionIDParameterID))
|
|
utils.BigEndian.WriteUint16(b, uint16(p.OriginalConnectionID.Len()))
|
|
b.Write(p.OriginalConnectionID.Bytes())
|
|
}
|
|
}
|
|
|
|
// String returns a string representation, intended for logging.
|
|
func (p *TransportParameters) String() string {
|
|
return fmt.Sprintf("&handshake.TransportParameters{OriginalConnectionID: %s, InitialMaxStreamDataBidiLocal: %#x, InitialMaxStreamDataBidiRemote: %#x, InitialMaxStreamDataUni: %#x, InitialMaxData: %#x, MaxBidiStreams: %d, MaxUniStreams: %d, IdleTimeout: %s}", p.OriginalConnectionID, p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreams, p.MaxUniStreams, p.IdleTimeout)
|
|
}
|