mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-14 07:55:07 +00:00
tidied up the first part of PacketCoding; made certain basic client-server communication still works
This commit is contained in:
parent
aac5e0a7ce
commit
c117f05165
1 changed files with 221 additions and 172 deletions
|
|
@ -2,11 +2,10 @@
|
||||||
package net.psforever.packet
|
package net.psforever.packet
|
||||||
|
|
||||||
import net.psforever.crypto.CryptoInterface
|
import net.psforever.crypto.CryptoInterface
|
||||||
import net.psforever.crypto.CryptoInterface._
|
|
||||||
import scodec.Attempt.{Successful, Failure}
|
import scodec.Attempt.{Successful, Failure}
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
import scodec.{DecodeResult, Err, Attempt, Codec}
|
import scodec.{Err, Attempt, Codec}
|
||||||
import scodec.codecs.{uint16L, uint8L, uint4L, bytes}
|
import scodec.codecs.{uint16L, uint8L, bytes}
|
||||||
|
|
||||||
/// Packet container base trait
|
/// Packet container base trait
|
||||||
sealed trait PlanetSidePacketContainer
|
sealed trait PlanetSidePacketContainer
|
||||||
|
|
@ -30,144 +29,110 @@ final case class ControlPacket(opcode : ControlPacketOpcode.Value,
|
||||||
packet : PlanetSideControlPacket) extends PlanetSidePacketContainer
|
packet : PlanetSideControlPacket) extends PlanetSidePacketContainer
|
||||||
|
|
||||||
object PacketCoding {
|
object PacketCoding {
|
||||||
/// A lower bound on the packet size
|
/**
|
||||||
final val PLANETSIDE_MIN_PACKET_SIZE = 1
|
* Access to the `ControlPacket` constructor.
|
||||||
|
* @param packet a `PlanetSideControlPacket`
|
||||||
|
* @return a `ControlPacket` wrapper
|
||||||
|
*/
|
||||||
|
def CreateControlPacket(packet : PlanetSideControlPacket) = ControlPacket(packet.opcode, packet)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a full and complete planetside packet as it would be sent on the wire, attempt to
|
* Access to the `CryptoPacket` constructor.
|
||||||
* decode it given an optional header and required payload. This function does all of the
|
* @param sequence na
|
||||||
* hard work of making decisions along the way in order to decode a planetside packet to
|
* @param packet a `PlanetSideCryptoPacket`
|
||||||
* completion.
|
* @return a `CryptoPacket` wrapper
|
||||||
*
|
|
||||||
* @param msg the raw packet
|
|
||||||
* @param cryptoState the current state of the connection's crypto. This is only used when decoding
|
|
||||||
* crypto packets as they do not have opcodes
|
|
||||||
* @return PlanetSidePacketContainer
|
|
||||||
*/
|
*/
|
||||||
def UnmarshalPacket(msg : ByteVector, cryptoState : CryptoPacketOpcode.Type) : Attempt[PlanetSidePacketContainer] = {
|
def CreateCryptoPacket(sequence : Int, packet : PlanetSideCryptoPacket) = CryptoPacket(sequence, packet)
|
||||||
// check for a minimum length
|
|
||||||
if(msg.length < PLANETSIDE_MIN_PACKET_SIZE)
|
|
||||||
return Attempt.failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
|
|
||||||
|
|
||||||
val firstByte = msg{0}
|
|
||||||
|
|
||||||
firstByte match {
|
|
||||||
// drop the first byte as control packets dont need it
|
|
||||||
case 0x00 => unmarshalControlPacket(msg.drop(1))
|
|
||||||
case _ => unmarshalFlaggedPacket(msg, cryptoState) // returns either EncryptedPacket or CryptoPacket
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to decode a packet without specifying a crypto packet state.
|
* Access to the `GamePacket` constructor.
|
||||||
* Mostly used when there is no crypto state available, such as tests.
|
* @param sequence na
|
||||||
*
|
* @param packet a `PlanetSideGamePacket`
|
||||||
* @param msg packet data bytes
|
* @return a `GamePacket` wrapper
|
||||||
* @return PlanetSidePacketContainer
|
|
||||||
*/
|
*/
|
||||||
def UnmarshalPacket(msg : ByteVector) : Attempt[PlanetSidePacketContainer] = {
|
def CreateGamePacket(sequence : Int, packet : PlanetSideGamePacket) = GamePacket(packet.opcode, sequence, packet)
|
||||||
UnmarshalPacket(msg, CryptoPacketOpcode.Ignore)
|
|
||||||
}
|
/* Marshalling and Encoding. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to UnmarshalPacket, but does not process any packet header and does not support
|
* Transforms a packet into `BitVector` representations of its component data and then re-assemble it.
|
||||||
* decoding of crypto packets. Mostly used in tests.
|
* Wraps around the encoding process for all valid packet container types.
|
||||||
*
|
* @param packet the packet to encode
|
||||||
* @param msg raw, unencrypted packet
|
* @return a `BitVector` translated from the packet's data
|
||||||
* @return PlanetSidePacket
|
|
||||||
*/
|
*/
|
||||||
def DecodePacket(msg : ByteVector) : Attempt[PlanetSidePacket] = {
|
|
||||||
// check for a minimum length
|
|
||||||
if(msg.length < PLANETSIDE_MIN_PACKET_SIZE)
|
|
||||||
return Attempt.failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
|
|
||||||
|
|
||||||
val firstByte = msg{0}
|
|
||||||
|
|
||||||
firstByte match {
|
|
||||||
// drop the first byte as control packets dont need it
|
|
||||||
case 0x00 => DecodeControlPacket(msg.drop(1))
|
|
||||||
case _ => DecodeGamePacket(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def MarshalPacket(packet : PlanetSidePacketContainer) : Attempt[BitVector] = {
|
def MarshalPacket(packet : PlanetSidePacketContainer) : Attempt[BitVector] = {
|
||||||
var flagsEncoded : BitVector = BitVector.empty
|
var flagsEncoded : BitVector = BitVector.empty //flags before everything in packet
|
||||||
var seqEncoded : BitVector = BitVector.empty
|
var seqEncoded : BitVector = BitVector.empty //control packets have a sequence number
|
||||||
var paddingEncoded : BitVector = BitVector.empty
|
var paddingEncoded : BitVector = BitVector.empty //encrypted packets need to be aligned in a certain way
|
||||||
var opcodeEncoded : BitVector = BitVector.empty
|
var payloadEncoded : BitVector = BitVector.empty //the packet itself as bits and bytes
|
||||||
var payloadEncoded : BitVector = BitVector.empty
|
|
||||||
|
|
||||||
var controlPacket = false
|
var controlPacket = false
|
||||||
var sequenceNum = 0
|
var sequenceNum = 0
|
||||||
|
//packet flags
|
||||||
// packet flags
|
|
||||||
var hasFlags = true
|
|
||||||
var secured = false
|
var secured = false
|
||||||
var packetType = PacketType.Crypto
|
var packetType = PacketType.Crypto
|
||||||
|
|
||||||
packet match {
|
packet match {
|
||||||
case GamePacket(opcode, seq, payload) =>
|
case GamePacket(_, seq, payload) =>
|
||||||
secured = false
|
|
||||||
packetType = PacketType.Normal
|
packetType = PacketType.Normal
|
||||||
sequenceNum = seq
|
sequenceNum = seq
|
||||||
|
|
||||||
EncodePacket(payload) match {
|
EncodePacket(payload) match {
|
||||||
case f @ Failure(e) => return f
|
case f @ Failure(_) => return f
|
||||||
case Successful(p) => payloadEncoded = p
|
case Successful(p) => payloadEncoded = p
|
||||||
}
|
}
|
||||||
case ControlPacket(opcode, payload) =>
|
|
||||||
|
case ControlPacket(_, payload) =>
|
||||||
controlPacket = true
|
controlPacket = true
|
||||||
|
|
||||||
EncodePacket(payload) match {
|
EncodePacket(payload) match {
|
||||||
case f @ Failure(e) => return f
|
case f @ Failure(_) => return f
|
||||||
case Successful(p) => payloadEncoded = p
|
case Successful(p) => payloadEncoded = p
|
||||||
}
|
}
|
||||||
|
|
||||||
case CryptoPacket(seq, payload) =>
|
case CryptoPacket(seq, payload) =>
|
||||||
secured = false
|
|
||||||
packetType = PacketType.Crypto
|
packetType = PacketType.Crypto
|
||||||
sequenceNum = seq
|
sequenceNum = seq
|
||||||
|
|
||||||
EncodePacket(payload) match {
|
EncodePacket(payload) match {
|
||||||
case f @ Failure(e) => return f
|
case f @ Failure(_) => return f
|
||||||
case Successful(p) => payloadEncoded = p
|
case Successful(p) => payloadEncoded = p
|
||||||
}
|
}
|
||||||
|
|
||||||
case EncryptedPacket(seq, payload) =>
|
case EncryptedPacket(seq, payload) =>
|
||||||
secured = true
|
secured = true
|
||||||
packetType = PacketType.Normal
|
packetType = PacketType.Normal
|
||||||
sequenceNum = seq
|
sequenceNum = seq
|
||||||
|
//encrypted packets need to be aligned to 4 bytes before encryption/decryption
|
||||||
// encrypted packets need to be aligned to 4 bytes before encryption/decryption
|
//first byte are flags, second is the sequence, and third is the pad
|
||||||
// first byte are flags, second and third the sequence, and fourth is the pad
|
|
||||||
paddingEncoded = hex"00".bits
|
paddingEncoded = hex"00".bits
|
||||||
payloadEncoded = payload.bits
|
payloadEncoded = payload.bits
|
||||||
}
|
}
|
||||||
|
|
||||||
val flags = PlanetSidePacketFlags(packetType, secured = secured)
|
//crypto packets DON'T have flags
|
||||||
|
|
||||||
// crypto packets DONT have flags
|
|
||||||
if(!controlPacket) {
|
if(!controlPacket) {
|
||||||
|
val flags = PlanetSidePacketFlags(packetType, secured = secured)
|
||||||
flagsEncoded = PlanetSidePacketFlags.codec.encode(flags).require
|
flagsEncoded = PlanetSidePacketFlags.codec.encode(flags).require
|
||||||
|
|
||||||
uint16L.encode(sequenceNum) match {
|
uint16L.encode(sequenceNum) match {
|
||||||
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal sequence in packet $packet: " + e.messageWithContext))
|
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal sequence in packet $packet: " + e.messageWithContext))
|
||||||
case Successful(p) => seqEncoded = p
|
case Successful(p) => seqEncoded = p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val finalPacket = flagsEncoded ++ seqEncoded ++ paddingEncoded ++ opcodeEncoded ++ payloadEncoded
|
Attempt.successful(flagsEncoded ++ seqEncoded ++ paddingEncoded ++ payloadEncoded)
|
||||||
Attempt.successful(finalPacket)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded method for transforming a control packet into its `BitVector` representation.
|
||||||
|
* @param packet the control packet to encode
|
||||||
|
* @return a `BitVector` translated from the packet's data
|
||||||
|
*/
|
||||||
def EncodePacket(packet : PlanetSideControlPacket) : Attempt[BitVector] = {
|
def EncodePacket(packet : PlanetSideControlPacket) : Attempt[BitVector] = {
|
||||||
val opcode = packet.opcode
|
val opcode = packet.opcode
|
||||||
var opcodeEncoded = BitVector.empty
|
|
||||||
var payloadEncoded = BitVector.empty
|
|
||||||
|
|
||||||
|
var opcodeEncoded = BitVector.empty
|
||||||
ControlPacketOpcode.codec.encode(opcode) match {
|
ControlPacketOpcode.codec.encode(opcode) match {
|
||||||
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal opcode in control packet $opcode: " + e.messageWithContext))
|
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal opcode in control packet $opcode: " + e.messageWithContext))
|
||||||
case Successful(p) => opcodeEncoded = p
|
case Successful(p) => opcodeEncoded = p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var payloadEncoded = BitVector.empty
|
||||||
encodePacket(packet) match {
|
encodePacket(packet) match {
|
||||||
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal control packet $packet: " + e.messageWithContext))
|
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal control packet $packet: " + e.messageWithContext))
|
||||||
case Successful(p) => payloadEncoded = p
|
case Successful(p) => payloadEncoded = p
|
||||||
|
|
@ -176,23 +141,33 @@ object PacketCoding {
|
||||||
Attempt.Successful(hex"00".bits ++ opcodeEncoded ++ payloadEncoded)
|
Attempt.Successful(hex"00".bits ++ opcodeEncoded ++ payloadEncoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded method for transforming a crypto packet into its `BitVector` representation.
|
||||||
|
* @param packet the crypto packet to encode
|
||||||
|
* @return a `BitVector` translated from the packet's data
|
||||||
|
*/
|
||||||
def EncodePacket(packet : PlanetSideCryptoPacket) : Attempt[BitVector] = {
|
def EncodePacket(packet : PlanetSideCryptoPacket) : Attempt[BitVector] = {
|
||||||
encodePacket(packet) match {
|
encodePacket(packet) match {
|
||||||
case Failure(e) => Attempt.failure(Err(s"Failed to marshal crypto packet $packet: " + e.messageWithContext))
|
case Failure(e) => Attempt.failure(Err(s"Failed to marshal crypto packet $packet: " + e.messageWithContext))
|
||||||
case s @ Successful(p) => s
|
case s @ Successful(_) => s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloaded method for transforming a game packet into its `BitVector` representation.
|
||||||
|
* @param packet the game packet to encode
|
||||||
|
* @return a `BitVector` translated from the packet's data
|
||||||
|
*/
|
||||||
def EncodePacket(packet : PlanetSideGamePacket) : Attempt[BitVector] = {
|
def EncodePacket(packet : PlanetSideGamePacket) : Attempt[BitVector] = {
|
||||||
val opcode = packet.opcode
|
val opcode = packet.opcode
|
||||||
var opcodeEncoded = BitVector.empty
|
|
||||||
var payloadEncoded = BitVector.empty
|
|
||||||
|
|
||||||
|
var opcodeEncoded = BitVector.empty
|
||||||
GamePacketOpcode.codec.encode(opcode) match {
|
GamePacketOpcode.codec.encode(opcode) match {
|
||||||
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal opcode in game packet $opcode: " + e.messageWithContext))
|
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal opcode in game packet $opcode: " + e.messageWithContext))
|
||||||
case Successful(p) => opcodeEncoded = p
|
case Successful(p) => opcodeEncoded = p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var payloadEncoded = BitVector.empty
|
||||||
encodePacket(packet) match {
|
encodePacket(packet) match {
|
||||||
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal game packet $packet: " + e.messageWithContext))
|
case Failure(e) => return Attempt.failure(Err(s"Failed to marshal game packet $packet: " + e.messageWithContext))
|
||||||
case Successful(p) => payloadEncoded = p
|
case Successful(p) => payloadEncoded = p
|
||||||
|
|
@ -201,17 +176,62 @@ object PacketCoding {
|
||||||
Attempt.Successful(opcodeEncoded ++ payloadEncoded)
|
Attempt.Successful(opcodeEncoded ++ payloadEncoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
def CreateControlPacket(packet : PlanetSideControlPacket) = ControlPacket(packet.opcode, packet)
|
/**
|
||||||
def CreateCryptoPacket(sequence : Int, packet : PlanetSideCryptoPacket) = CryptoPacket(sequence, packet)
|
* Calls the packet-specific encode function.
|
||||||
def CreateGamePacket(sequence : Int, packet : PlanetSideGamePacket) = GamePacket(packet.opcode, sequence, packet)
|
* Lowest encode call.
|
||||||
|
* @param packet the packet to encode
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
* @return a `BitVector` translated from the packet's data
|
||||||
|
*/
|
||||||
private def encodePacket(packet : PlanetSidePacket) : Attempt[BitVector] = packet.encode
|
private def encodePacket(packet : PlanetSidePacket) : Attempt[BitVector] = packet.encode
|
||||||
|
|
||||||
private def unmarshalFlaggedPacket(msg : ByteVector, cryptoState : CryptoPacketOpcode.Type) : Attempt[PlanetSidePacketContainer] = {
|
/* Unmarshalling and Decoding. */
|
||||||
val decodedFlags = Codec.decode[PlanetSidePacketFlags](BitVector(msg))
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lower bound on the packet size
|
||||||
|
*/
|
||||||
|
final val PLANETSIDE_MIN_PACKET_SIZE = 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms `BitVector` data into a PlanetSide packet.<br>
|
||||||
|
* <br>
|
||||||
|
* Given a full and complete planetside packet as it would be sent on the wire, attempt to
|
||||||
|
* decode it given an optional header and required payload. This function does all of the
|
||||||
|
* hard work of making decisions along the way in order to decode a planetside packet to
|
||||||
|
* completion.
|
||||||
|
* @param msg the raw packet
|
||||||
|
* @param cryptoState the current state of the connection's crypto. This is only used when decoding
|
||||||
|
* crypto packets as they do not have opcodes
|
||||||
|
* @return `PlanetSidePacketContainer`
|
||||||
|
*/
|
||||||
|
def UnmarshalPacket(msg : ByteVector, cryptoState : CryptoPacketOpcode.Type) : Attempt[PlanetSidePacketContainer] = {
|
||||||
|
if(msg.length < PLANETSIDE_MIN_PACKET_SIZE)
|
||||||
|
return Attempt.failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
|
||||||
|
|
||||||
|
val firstByte = msg{0}
|
||||||
|
firstByte match {
|
||||||
|
case 0x00 => unmarshalControlPacket(msg.drop(1)) //control packets dont need the first byte
|
||||||
|
case _ => unmarshalFlaggedPacket(msg, cryptoState) //either EncryptedPacket or CryptoPacket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to decode a packet without specifying a crypto packet state.
|
||||||
|
* Mostly used when there is no crypto state available, such as tests.
|
||||||
|
* @param msg packet data bytes
|
||||||
|
* @return `PlanetSidePacketContainer`
|
||||||
|
*/
|
||||||
|
def UnmarshalPacket(msg : ByteVector) : Attempt[PlanetSidePacketContainer] = UnmarshalPacket(msg, CryptoPacketOpcode.Ignore)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle decoding for a packet that is not a control packet.
|
||||||
|
* It may just be encrypted or it may be involved in the encryption process itself.
|
||||||
|
* @param msg the packet
|
||||||
|
* @param cryptoState the current state of the connection's crypto
|
||||||
|
* @return a `PlanetSidePacketContainer`
|
||||||
|
*/
|
||||||
|
private def unmarshalFlaggedPacket(msg : ByteVector, cryptoState : CryptoPacketOpcode.Type) : Attempt[PlanetSidePacketContainer] = {
|
||||||
|
//get the flags
|
||||||
|
val decodedFlags = Codec.decode[PlanetSidePacketFlags](BitVector(msg))
|
||||||
decodedFlags match {
|
decodedFlags match {
|
||||||
case Failure(e) =>
|
case Failure(e) =>
|
||||||
return Attempt.failure(Err("Failed to parse packet flags: " + e.message))
|
return Attempt.failure(Err("Failed to parse packet flags: " + e.message))
|
||||||
|
|
@ -219,112 +239,76 @@ object PacketCoding {
|
||||||
}
|
}
|
||||||
|
|
||||||
val flags = decodedFlags.require.value
|
val flags = decodedFlags.require.value
|
||||||
val rest = decodedFlags.require.remainder
|
|
||||||
val packetType = flags.packetType
|
val packetType = flags.packetType
|
||||||
|
|
||||||
// perform a quick reject of weird packet types
|
|
||||||
packetType match {
|
packetType match {
|
||||||
case PacketType.Crypto => ;
|
case PacketType.Normal =>
|
||||||
case PacketType.Normal => ;
|
if(!flags.secured) { //support normal packets only if they are encrypted
|
||||||
case default =>
|
return Attempt.failure(Err("Unsupported packet type: normal packets must be encryped"))
|
||||||
|
}
|
||||||
|
|
||||||
|
case PacketType.Crypto =>
|
||||||
|
if(flags.secured) { //support crypto packets only if they are not encrypted
|
||||||
|
return Attempt.failure(Err("Unsupported packet type: crypto packets must be unencrypted"))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _ =>
|
||||||
return Attempt.failure(Err("Unsupported packet type: " + flags.packetType.toString))
|
return Attempt.failure(Err("Unsupported packet type: " + flags.packetType.toString))
|
||||||
}
|
}
|
||||||
|
|
||||||
// we only support normal packets if they are encrypted
|
//all packets have a two byte sequence ID
|
||||||
if(packetType == PacketType.Normal && !flags.secured)
|
val decodedSeq = uint16L.decode(decodedFlags.require.remainder) //TODO: make this a codec for reuse
|
||||||
return Attempt.failure(Err("Unsupported packet type: normal packets must be encryped"))
|
|
||||||
|
|
||||||
// we only support crypto packets if they are not encrypted
|
|
||||||
if(packetType == PacketType.Crypto && flags.secured)
|
|
||||||
return Attempt.failure(Err("Unsupported packet type: crypto packets must be unencrypted"))
|
|
||||||
|
|
||||||
// all packets have a two byte sequence ID
|
|
||||||
// TODO: make this a codec for reuse
|
|
||||||
val decodedSeq = uint16L.decode(rest)
|
|
||||||
|
|
||||||
decodedSeq match {
|
decodedSeq match {
|
||||||
case Failure(e) =>
|
case Failure(e) =>
|
||||||
return Attempt.failure(Err("Failed to parse packet sequence number: " + e.message))
|
return Attempt.failure(Err("Failed to parse packet sequence number: " + e.message))
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
|
||||||
val sequence = decodedSeq.require.value
|
val sequence = decodedSeq.require.value
|
||||||
var payload = decodedSeq.require.remainder.toByteVector
|
val payload = decodedSeq.require.remainder.toByteVector
|
||||||
|
|
||||||
// encrypted packets must be 4-byte aligned
|
|
||||||
if(flags.secured) {
|
|
||||||
payload = payload.drop(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
packetType match {
|
packetType match {
|
||||||
case PacketType.Crypto =>
|
case PacketType.Crypto =>
|
||||||
unmarshalCryptoPacket(cryptoState, sequence, payload)
|
unmarshalCryptoPacket(cryptoState, sequence, payload)
|
||||||
case PacketType.Normal =>
|
case PacketType.Normal =>
|
||||||
unmarshalEncryptedPacket(sequence, payload)
|
unmarshalEncryptedPacket(sequence, payload.drop(1)) //payload is 4-byte aligned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle decoding for a control packet.
|
||||||
|
* @param msg the packet
|
||||||
|
* @return a `ControlPacket`
|
||||||
|
*/
|
||||||
private def unmarshalControlPacket(msg : ByteVector) : Attempt[ControlPacket] = {
|
private def unmarshalControlPacket(msg : ByteVector) : Attempt[ControlPacket] = {
|
||||||
val packet = DecodeControlPacket(msg)
|
DecodeControlPacket(msg) match {
|
||||||
|
case f @ Failure(_) => f
|
||||||
packet match {
|
|
||||||
// just return the failure
|
|
||||||
case f @ Failure(e) => f
|
|
||||||
case Successful(p) =>
|
case Successful(p) =>
|
||||||
Attempt.successful(CreateControlPacket(p))
|
Attempt.successful(CreateControlPacket(p))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def DecodeControlPacket(msg : ByteVector) : Attempt[PlanetSideControlPacket] = {
|
/**
|
||||||
val opcode = ControlPacketOpcode.codec.decode(msg.bits)
|
* Handle decoding for a game packet.
|
||||||
|
* @param sequence na
|
||||||
opcode match {
|
* @param msg the packet data
|
||||||
case Failure(e) =>
|
* @return a `GamePacket`
|
||||||
Attempt.failure(Err("Failed to decode control packet's opcode: " + e.message))
|
*/
|
||||||
case Successful(op) =>
|
|
||||||
val packet = ControlPacketOpcode.getPacketDecoder(op.value)(op.remainder)
|
|
||||||
|
|
||||||
packet match {
|
|
||||||
case Failure(e) =>
|
|
||||||
Attempt.failure(Err(f"Failed to parse control packet ${op.value}: " + e.messageWithContext))
|
|
||||||
case Successful(p) =>
|
|
||||||
Attempt.successful(p.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def unmarshalGamePacket(sequence : Int, msg : ByteVector) : Attempt[GamePacket] = {
|
private def unmarshalGamePacket(sequence : Int, msg : ByteVector) : Attempt[GamePacket] = {
|
||||||
val packet = DecodeGamePacket(msg)
|
DecodeGamePacket(msg) match {
|
||||||
|
case f @ Failure(_) => f
|
||||||
packet match {
|
|
||||||
case f @ Failure(e) => f
|
|
||||||
case Successful(p) =>
|
case Successful(p) =>
|
||||||
Attempt.successful(CreateGamePacket(sequence, p))
|
Attempt.successful(CreateGamePacket(sequence, p))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def DecodeGamePacket(msg : ByteVector) : Attempt[PlanetSideGamePacket] = {
|
/**
|
||||||
val opcode = GamePacketOpcode.codec.decode(msg.bits)
|
* Handle decoding for a crypto packet.
|
||||||
|
* @param state the current state of the connection's crypto
|
||||||
opcode match {
|
* @param sequence na
|
||||||
case Failure(e) =>
|
* @param payload the packet data
|
||||||
return Attempt.failure(Err("Failed to decode game packet's opcode: " + e.message))
|
* @return a `CryptoPacket`
|
||||||
case _ =>
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
val packet = GamePacketOpcode.getPacketDecoder(opcode.require.value)(opcode.require.remainder)
|
|
||||||
|
|
||||||
packet match {
|
|
||||||
case Failure(e) =>
|
|
||||||
Attempt.failure(Err(f"Failed to parse game packet 0x${opcode.require.value.id}%02x: " + e.messageWithContext))
|
|
||||||
case Successful(p) => Attempt.successful(p.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def unmarshalCryptoPacket(state : CryptoPacketOpcode.Type, sequence : Int, payload : ByteVector) : Attempt[CryptoPacket] = {
|
private def unmarshalCryptoPacket(state : CryptoPacketOpcode.Type, sequence : Int, payload : ByteVector) : Attempt[CryptoPacket] = {
|
||||||
val packet = CryptoPacketOpcode.getPacketDecoder(state)(payload.bits)
|
CryptoPacketOpcode.getPacketDecoder(state)(payload.bits) match {
|
||||||
|
|
||||||
packet match {
|
|
||||||
case Successful(a) =>
|
case Successful(a) =>
|
||||||
Attempt.successful(CryptoPacket(sequence, a.value))
|
Attempt.successful(CryptoPacket(sequence, a.value))
|
||||||
case Failure(e) =>
|
case Failure(e) =>
|
||||||
|
|
@ -332,13 +316,78 @@ object PacketCoding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle decoding for an encrypted packet.
|
||||||
|
* That is, it's already encrypted.
|
||||||
|
* Just repackage the data.
|
||||||
|
* @param sequence na
|
||||||
|
* @param payload the packet data
|
||||||
|
* @return an `EncryptedPacket`
|
||||||
|
*/
|
||||||
private def unmarshalEncryptedPacket(sequence : Int, payload : ByteVector) : Attempt[EncryptedPacket] = {
|
private def unmarshalEncryptedPacket(sequence : Int, payload : ByteVector) : Attempt[EncryptedPacket] = {
|
||||||
Attempt.successful(EncryptedPacket(sequence, payload))
|
Attempt.successful(EncryptedPacket(sequence, payload))
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
/**
|
||||||
// Packet Crypto
|
* Similar to `UnmarshalPacket`, but does not process any packet header and does not support decoding of crypto packets.
|
||||||
///////////////////////////////////////////////////////////
|
* Mostly used in tests.
|
||||||
|
* @param msg raw, unencrypted packet
|
||||||
|
* @return `PlanetSidePacket`
|
||||||
|
*/
|
||||||
|
def DecodePacket(msg : ByteVector) : Attempt[PlanetSidePacket] = {
|
||||||
|
if(msg.length < PLANETSIDE_MIN_PACKET_SIZE)
|
||||||
|
return Attempt.failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
|
||||||
|
|
||||||
|
val firstByte = msg{0}
|
||||||
|
firstByte match {
|
||||||
|
case 0x00 => DecodeControlPacket(msg.drop(1)) //control packets dont need the first byte
|
||||||
|
case _ => DecodeGamePacket(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform a `BitVector` into a control packet.
|
||||||
|
* @param msg the the raw data to decode
|
||||||
|
* @return a `PlanetSideControlPacket`
|
||||||
|
*/
|
||||||
|
def DecodeControlPacket(msg : ByteVector) : Attempt[PlanetSideControlPacket] = {
|
||||||
|
ControlPacketOpcode.codec.decode(msg.bits) match {
|
||||||
|
case Failure(e) =>
|
||||||
|
Attempt.failure(Err("Failed to decode control packet's opcode: " + e.message))
|
||||||
|
|
||||||
|
case Successful(op) =>
|
||||||
|
ControlPacketOpcode.getPacketDecoder(op.value)(op.remainder) match {
|
||||||
|
case Failure(e) =>
|
||||||
|
Attempt.failure(Err(f"Failed to parse control packet ${op.value}: " + e.messageWithContext))
|
||||||
|
|
||||||
|
case Successful(p) =>
|
||||||
|
Attempt.successful(p.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform a `BitVector` into a game packet.
|
||||||
|
* @param msg the the raw data to decode
|
||||||
|
* @return a `PlanetSideGamePacket`
|
||||||
|
*/
|
||||||
|
def DecodeGamePacket(msg : ByteVector) : Attempt[PlanetSideGamePacket] = {
|
||||||
|
GamePacketOpcode.codec.decode(msg.bits) match {
|
||||||
|
case Failure(e) =>
|
||||||
|
Attempt.failure(Err("Failed to decode game packet's opcode: " + e.message))
|
||||||
|
|
||||||
|
case Successful(opcode) =>
|
||||||
|
GamePacketOpcode.getPacketDecoder(opcode.value)(opcode.remainder) match {
|
||||||
|
case Failure(e) =>
|
||||||
|
Attempt.failure(Err(f"Failed to parse game packet 0x${opcode.value.id}%02x: " + e.messageWithContext))
|
||||||
|
|
||||||
|
case Successful(p) =>
|
||||||
|
Attempt.successful(p.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encrypting and Decrypting. */
|
||||||
|
|
||||||
def encryptPacket(crypto : CryptoInterface.CryptoStateWithMAC, packet : PlanetSidePacketContainer) : Attempt[EncryptedPacket] = {
|
def encryptPacket(crypto : CryptoInterface.CryptoStateWithMAC, packet : PlanetSidePacketContainer) : Attempt[EncryptedPacket] = {
|
||||||
// TODO XXX: this is bad. rework
|
// TODO XXX: this is bad. rework
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue