| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- package protocol
- import (
- "crypto/aes"
- "crypto/cipher"
- "encoding/binary"
- "hash/fnv"
- "time"
- "github.com/v2ray/v2ray-core/common/errors"
- "github.com/v2ray/v2ray-core/common/log"
- v2net "github.com/v2ray/v2ray-core/common/net"
- "github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
- )
- type VMessUDP struct {
- user user.ID
- version byte
- token uint16
- address v2net.Address
- data []byte
- }
- func ReadVMessUDP(buffer []byte, userset user.UserSet) (*VMessUDP, error) {
- userHash := buffer[:user.IDBytesLen]
- userId, timeSec, valid := userset.GetUser(userHash)
- if !valid {
- return nil, errors.NewAuthenticationError(userHash)
- }
- buffer = buffer[user.IDBytesLen:]
- aesCipher, err := aes.NewCipher(userId.CmdKey())
- if err != nil {
- return nil, err
- }
- aesStream := cipher.NewCFBDecrypter(aesCipher, user.Int64Hash(timeSec))
- aesStream.XORKeyStream(buffer, buffer)
- fnvHash := binary.BigEndian.Uint32(buffer[:4])
- fnv1a := fnv.New32a()
- fnv1a.Write(buffer[4:])
- fnvHashActual := fnv1a.Sum32()
- if fnvHash != fnvHashActual {
- log.Warning("Unexpected fhv hash %d, should be %d", fnvHashActual, fnvHash)
- return nil, errors.NewCorruptedPacketError()
- }
- buffer = buffer[4:]
- vmess := &VMessUDP{
- user: *userId,
- version: buffer[0],
- token: binary.BigEndian.Uint16(buffer[1:3]),
- }
- // buffer[3] is reserved
- port := binary.BigEndian.Uint16(buffer[4:6])
- addrType := buffer[6]
- var address v2net.Address
- switch addrType {
- case addrTypeIPv4:
- address = v2net.IPAddress(buffer[7:11], port)
- buffer = buffer[11:]
- case addrTypeIPv6:
- address = v2net.IPAddress(buffer[7:23], port)
- buffer = buffer[23:]
- case addrTypeDomain:
- domainLength := buffer[7]
- domain := string(buffer[8 : 8+domainLength])
- address = v2net.DomainAddress(domain, port)
- buffer = buffer[8+domainLength:]
- default:
- log.Warning("Unexpected address type %d", addrType)
- return nil, errors.NewCorruptedPacketError()
- }
- vmess.address = address
- vmess.data = buffer
- return vmess, nil
- }
- func (vmess *VMessUDP) ToBytes(idHash user.CounterHash, randomRangeInt64 user.RandomInt64InRange, buffer []byte) []byte {
- if buffer == nil {
- buffer = make([]byte, 0, 2*1024)
- }
- counter := randomRangeInt64(time.Now().UTC().Unix(), 30)
- hash := idHash.Hash(vmess.user.Bytes[:], counter)
- buffer = append(buffer, hash...)
- encryptBegin := 16
- // Placeholder for fnv1a hash
- buffer = append(buffer, byte(0), byte(0), byte(0), byte(0))
- fnvHash := 16
- fnvHashBegin := 20
- buffer = append(buffer, vmess.version)
- buffer = append(buffer, byte(vmess.token>>8), byte(vmess.token))
- buffer = append(buffer, byte(0x00))
- buffer = append(buffer, vmess.address.PortBytes()...)
- switch {
- case vmess.address.IsIPv4():
- buffer = append(buffer, addrTypeIPv4)
- buffer = append(buffer, vmess.address.IP()...)
- case vmess.address.IsIPv6():
- buffer = append(buffer, addrTypeIPv6)
- buffer = append(buffer, vmess.address.IP()...)
- case vmess.address.IsDomain():
- buffer = append(buffer, addrTypeDomain)
- buffer = append(buffer, byte(len(vmess.address.Domain())))
- buffer = append(buffer, []byte(vmess.address.Domain())...)
- }
- buffer = append(buffer, vmess.data...)
- fnv1a := fnv.New32a()
- fnv1a.Write(buffer[fnvHashBegin:])
- fnvHashValue := fnv1a.Sum32()
- buffer[fnvHash] = byte(fnvHashValue >> 24)
- buffer[fnvHash+1] = byte(fnvHashValue >> 16)
- buffer[fnvHash+2] = byte(fnvHashValue >> 8)
- buffer[fnvHash+3] = byte(fnvHashValue)
- aesCipher, err := aes.NewCipher(vmess.user.CmdKey())
- if err != nil {
- log.Error("VMess failed to create AES cipher: %v", err)
- return nil
- }
- aesStream := cipher.NewCFBEncrypter(aesCipher, user.Int64Hash(counter))
- aesStream.XORKeyStream(buffer[encryptBegin:], buffer[encryptBegin:])
- return buffer
- }
|