nex-go/packet_v1.go
2023-06-07 23:13:41 -04:00

403 lines
11 KiB
Go

package nex
import (
"bytes"
"crypto/hmac"
"crypto/md5"
"encoding/binary"
"errors"
"fmt"
)
// Magic is the expected PRUDPv1 magic number
var Magic = []byte{0xEA, 0xD0}
// OptionAllFunctions is used with OptionSupportedFunctions to support all methods
var OptionAllFunctions = 0xFFFFFFFF
// OptionSupportedFunctions is the ID for the Supported Functions option in PRUDP v1 packets
var OptionSupportedFunctions uint8 = 0
// OptionConnectionSignature is the ID for the Connection Signature option in PRUDP v1 packets
var OptionConnectionSignature uint8 = 1
// OptionFragmentID is the ID for the Fragment ID option in PRUDP v1 packets
var OptionFragmentID uint8 = 2
// OptionInitialSequenceID is the ID for the initial sequence ID option in PRUDP v1 packets
var OptionInitialSequenceID uint8 = 3
// OptionMaxSubstreamID is the ID for the max substream ID option in PRUDP v1 packets
var OptionMaxSubstreamID uint8 = 4
// PacketV1 reresents a PRUDPv1 packet
type PacketV1 struct {
Packet
magic []byte
substreamID uint8
prudpProtocolMinorVersion int
supportedFunctions int
initialSequenceID uint16
maximumSubstreamID uint8
}
// SetSubstreamID sets the packet substream ID
func (packet *PacketV1) SetSubstreamID(substreamID uint8) {
packet.substreamID = substreamID
}
// SubstreamID returns the packet substream ID
func (packet *PacketV1) SubstreamID() uint8 {
return packet.substreamID
}
// PRUDPProtocolMinorVersion returns the packet PRUDP minor version
func (packet *PacketV1) PRUDPProtocolMinorVersion() int {
return packet.prudpProtocolMinorVersion
}
// SetPRUDPProtocolMinorVersion sets the packet PRUDP minor version
func (packet *PacketV1) SetPRUDPProtocolMinorVersion(prudpProtocolMinorVersion int) {
packet.prudpProtocolMinorVersion = prudpProtocolMinorVersion
}
// SetSupportedFunctions sets the packet supported functions flags
func (packet *PacketV1) SetSupportedFunctions(supportedFunctions int) {
packet.supportedFunctions = supportedFunctions
}
// SupportedFunctions returns the packet supported functions flags
func (packet *PacketV1) SupportedFunctions() int {
return packet.supportedFunctions
}
// SetInitialSequenceID sets the packet initial sequence ID for unreliable packets
func (packet *PacketV1) SetInitialSequenceID(initialSequenceID uint16) {
packet.initialSequenceID = initialSequenceID
}
// InitialSequenceID returns the packet initial sequence ID for unreliable packets
func (packet *PacketV1) InitialSequenceID() uint16 {
return packet.initialSequenceID
}
// SetMaximumSubstreamID sets the packet maximum substream ID
func (packet *PacketV1) SetMaximumSubstreamID(maximumSubstreamID uint8) {
packet.maximumSubstreamID = maximumSubstreamID
}
// MaximumSubstreamID returns the packet maximum substream ID
func (packet *PacketV1) MaximumSubstreamID() uint8 {
return packet.maximumSubstreamID
}
// Decode decodes the packet
func (packet *PacketV1) Decode() error {
stream := NewStreamIn(packet.Data(), packet.Sender().Server())
if len(stream.Bytes()[stream.ByteOffset():]) < 2 {
return errors.New("Failed to read PRUDPv1 magic. Not have enough data")
}
packet.magic = stream.ReadBytesNext(2)
if !bytes.Equal(packet.magic, Magic) {
return fmt.Errorf("Invalid PRUDPv1 magic. Expected %x, got %x", Magic, packet.magic)
}
version, err := stream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 version. %s", err.Error())
}
packet.SetVersion(version)
if packet.Version() != 1 {
return fmt.Errorf("Invalid PRUDPv1 version. Expected 1, got %d", packet.Version())
}
optionsLength, err := stream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 options length. %s", err.Error())
}
payloadSize, err := stream.ReadUInt16LE()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 payload size. %s", err.Error())
}
source, err := stream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 source. %s", err.Error())
}
packet.SetSource(source)
destination, err := stream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 destination. %s", err.Error())
}
packet.SetDestination(destination)
typeFlags, err := stream.ReadUInt16LE()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 type-flags. %s", err.Error())
}
packet.SetType(typeFlags & 0xF)
if _, ok := validTypes[packet.Type()]; !ok {
return errors.New("Invalid PRUDP packet type")
}
packet.SetFlags(typeFlags >> 4)
sessionID, err := stream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 session ID. %s", err.Error())
}
packet.SetSessionID(sessionID)
substreamID, err := stream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 substream ID. %s", err.Error())
}
packet.SetSubstreamID(substreamID)
sequenceID, err := stream.ReadUInt16LE()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 sequence ID. %s", err.Error())
}
packet.SetSequenceID(sequenceID)
if len(stream.Bytes()[stream.ByteOffset():]) < 16 {
return errors.New("Failed to read PRUDPv1 packet signature. Not have enough data")
}
packet.SetSignature(stream.ReadBytesNext(16))
if len(packet.Data()[stream.ByteOffset():]) < int(optionsLength) {
return errors.New("[PRUDPv1] Packet specific data size does not match")
}
options := stream.ReadBytesNext(int64(optionsLength))
err = packet.decodeOptions(options)
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 options. %s", err.Error())
}
if payloadSize > 0 {
if len(packet.Data()[stream.ByteOffset():]) < int(payloadSize) {
return errors.New("Failed to read PRUDPv1 packet payload. Not enough data")
}
payloadCrypted := stream.ReadBytesNext(int64(payloadSize))
packet.SetPayload(payloadCrypted)
if packet.Type() == DataPacket && !packet.HasFlag(FlagMultiAck) {
ciphered := make([]byte, payloadSize)
packet.Sender().Decipher().XORKeyStream(ciphered, payloadCrypted)
request := NewRMCRequest()
err := request.FromBytes(ciphered)
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 RMC request. %s", err.Error())
}
packet.rmcRequest = request
}
}
calculatedSignature := packet.calculateSignature(packet.Data()[2:14], packet.Sender().ServerConnectionSignature(), options, packet.Payload())
if !bytes.Equal(calculatedSignature, packet.Signature()) {
logger.Error("PRUDPv1 calculated signature did not match")
}
return nil
}
// Bytes encodes the packet and returns a byte array
func (packet *PacketV1) Bytes() []byte {
if packet.Type() == DataPacket {
if !packet.HasFlag(FlagMultiAck) {
payload := packet.Payload()
if payload != nil || len(payload) > 0 {
payloadSize := len(payload)
encrypted := make([]byte, payloadSize)
packet.Sender().Cipher().XORKeyStream(encrypted, payload)
packet.SetPayload(encrypted)
}
}
if !packet.HasFlag(FlagHasSize) {
packet.AddFlag(FlagHasSize)
}
}
var typeFlags uint16 = packet.Type() | packet.Flags()<<4
stream := NewStreamOut(packet.Sender().Server())
stream.WriteUInt16LE(0xD0EA) // v1 magic
stream.WriteUInt8(1)
options := packet.encodeOptions()
optionsLength := len(options)
stream.WriteUInt8(uint8(optionsLength))
stream.WriteUInt16LE(uint16(len(packet.Payload())))
stream.WriteUInt8(packet.Source())
stream.WriteUInt8(packet.Destination())
stream.WriteUInt16LE(typeFlags)
stream.WriteUInt8(packet.SessionID())
stream.WriteUInt8(packet.SubstreamID())
stream.WriteUInt16LE(packet.SequenceID())
signature := packet.calculateSignature(stream.Bytes()[2:14], packet.Sender().ClientConnectionSignature(), options, packet.Payload())
stream.Grow(int64(len(signature)))
stream.WriteBytesNext(signature)
if optionsLength > 0 {
stream.Grow(int64(optionsLength))
stream.WriteBytesNext(options)
}
payload := packet.Payload()
payloadLength := len(payload)
if payload != nil && payloadLength > 0 {
stream.Grow(int64(payloadLength))
stream.WriteBytesNext(payload)
}
return stream.Bytes()
}
func (packet *PacketV1) decodeOptions(options []byte) error {
optionsStream := NewStreamIn(options, packet.Sender().Server())
for optionsStream.ByteOffset() != optionsStream.ByteCapacity() {
optionID, err := optionsStream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 option ID. %s", err.Error())
}
optionSize, err := optionsStream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 option size for option ID %d. %s", optionID, err.Error())
}
switch optionID {
case OptionSupportedFunctions:
supportedFunctions, err := optionsStream.ReadUInt32LE()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 option supported functions. %s", err.Error())
}
packet.sender.SetPRUDPProtocolMinorVersion(int(supportedFunctions & 0xFF))
packet.sender.SetSupportedFunctions(int(supportedFunctions >> 8))
case OptionConnectionSignature:
packet.SetConnectionSignature(optionsStream.ReadBytesNext(int64(optionSize)))
case OptionFragmentID:
fragmentID, err := optionsStream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 option fragment ID. %s", err.Error())
}
packet.SetFragmentID(fragmentID)
case OptionInitialSequenceID:
sequenceID, err := optionsStream.ReadUInt16LE()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 option sequence ID. %s", err.Error())
}
packet.SetInitialSequenceID(sequenceID)
case OptionMaxSubstreamID:
maximumSubstreamID, err := optionsStream.ReadUInt8()
if err != nil {
return fmt.Errorf("Failed to read PRUDPv1 option maximum substream ID. %s", err.Error())
}
packet.SetMaximumSubstreamID(maximumSubstreamID)
}
}
return nil
}
func (packet *PacketV1) encodeOptions() []byte {
stream := NewStreamOut(packet.Sender().Server())
if packet.Type() == SynPacket || packet.Type() == ConnectPacket {
stream.WriteUInt8(OptionSupportedFunctions)
stream.WriteUInt8(4)
stream.WriteUInt32LE(uint32(packet.prudpProtocolMinorVersion) | uint32(packet.supportedFunctions<<8))
stream.WriteUInt8(OptionConnectionSignature)
stream.WriteUInt8(16)
stream.Grow(16)
stream.WriteBytesNext(packet.ConnectionSignature())
if packet.Type() == ConnectPacket {
stream.WriteUInt8(OptionInitialSequenceID)
stream.WriteUInt8(2)
stream.WriteUInt16LE(packet.initialSequenceID)
}
stream.WriteUInt8(OptionMaxSubstreamID)
stream.WriteUInt8(1)
stream.WriteUInt8(packet.maximumSubstreamID)
} else if packet.Type() == DataPacket {
stream.WriteUInt8(OptionFragmentID)
stream.WriteUInt8(1)
stream.WriteUInt8(packet.FragmentID())
}
return stream.Bytes()
}
func (packet *PacketV1) calculateSignature(header []byte, connectionSignature []byte, options []byte, payload []byte) []byte {
key := packet.Sender().SignatureKey()
sessionKey := packet.Sender().SessionKey()
signatureBase := make([]byte, 4)
binary.LittleEndian.PutUint32(signatureBase, uint32(packet.Sender().SignatureBase()))
mac := hmac.New(md5.New, key)
mac.Write(header[4:])
mac.Write(sessionKey)
mac.Write(signatureBase)
mac.Write(connectionSignature)
mac.Write(options)
mac.Write(payload)
return mac.Sum(nil)
}
// NewPacketV1 returns a new PRUDPv1 packet
func NewPacketV1(client *Client, data []byte) (*PacketV1, error) {
packet := NewPacket(client, data)
packetv1 := PacketV1{Packet: packet}
if data != nil {
err := packetv1.Decode()
if err != nil {
return &PacketV1{}, fmt.Errorf("Failed to decode PRUDPv1 packet. %s", err.Error())
}
}
return &packetv1, nil
}