Merge pull request #770 from jgillich/unknown30

add Unknown30 decoder, allow non-advanced packets
This commit is contained in:
Mazo 2021-04-17 19:50:47 +01:00 committed by GitHub
commit 6fbe35b15f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 92 deletions

View file

@ -129,7 +129,7 @@ object MiddlewareActor {
* Do nothing. * Do nothing.
* Wait to be told to do something. * Wait to be told to do something.
*/ */
private def doNothing(): Unit = { } private def doNothing(): Unit = {}
} }
/** /**
@ -182,7 +182,8 @@ class MiddlewareActor(
val outQueueBundled: mutable.Queue[PlanetSidePacket] = mutable.Queue() val outQueueBundled: mutable.Queue[PlanetSidePacket] = mutable.Queue()
/** Latest outbound sequence number; /** Latest outbound sequence number;
* the current sequence is one less than this number */ * the current sequence is one less than this number
*/
var outSequence = 0 var outSequence = 0
/** /**
@ -202,7 +203,8 @@ class MiddlewareActor(
} }
/** Latest outbound subslot number; /** Latest outbound subslot number;
* the current subslot is one less than this number */ * the current subslot is one less than this number
*/
var outSubslot = 0 var outSubslot = 0
/** /**
@ -224,12 +226,13 @@ class MiddlewareActor(
/** /**
* Do not bundle these packets together with other packets * Do not bundle these packets together with other packets
*/ */
val packetsBundledByThemselves: List[PlanetSidePacket=>Boolean] = List( val packetsBundledByThemselves: List[PlanetSidePacket => Boolean] = List(
MiddlewareActor.keepAliveMessageGuard, MiddlewareActor.keepAliveMessageGuard,
MiddlewareActor.characterInfoMessageGuard MiddlewareActor.characterInfoMessageGuard
) )
val smpHistoryLength: Int = 100 val smpHistoryLength: Int = 100
/** History of created `SlottedMetaPacket`s. /** History of created `SlottedMetaPacket`s.
* In case the client does not register receiving a packet by checking against packet subslot index numbers, * In case the client does not register receiving a packet by checking against packet subslot index numbers,
* it will dispatch a `RelatedA` packet, * it will dispatch a `RelatedA` packet,
@ -239,24 +242,32 @@ class MiddlewareActor(
* The client and server supposedly maintain reciprocating mechanisms. * The client and server supposedly maintain reciprocating mechanisms.
*/ */
val preparedSlottedMetaPackets: Array[SlottedMetaPacket] = new Array[SlottedMetaPacket](smpHistoryLength) val preparedSlottedMetaPackets: Array[SlottedMetaPacket] = new Array[SlottedMetaPacket](smpHistoryLength)
var nextSmpIndex: Int = 0 var nextSmpIndex: Int = 0
var acceptedSmpSubslot: Int = 0 var acceptedSmpSubslot: Int = 0
/** end of life stat */ /** end of life stat */
var timesInReorderQueue: Int = 0 var timesInReorderQueue: Int = 0
/** end of life stat */ /** end of life stat */
var timesSubslotMissing: Int = 0 var timesSubslotMissing: Int = 0
/** Delay between runs of the packet bundler/resolver timer (ms); /** Delay between runs of the packet bundler/resolver timer (ms);
* 250ms per network update (client upstream), so 10 runs of this bundling code every update */ * 250ms per network update (client upstream), so 10 runs of this bundling code every update
*/
val packetProcessorDelay = Config.app.network.middleware.packetBundlingDelay val packetProcessorDelay = Config.app.network.middleware.packetBundlingDelay
/** Timer that handles the bundling and throttling of outgoing packets and resolves disorganized inbound packets */ /** Timer that handles the bundling and throttling of outgoing packets and resolves disorganized inbound packets */
var packetProcessor: Cancellable = Default.Cancellable var packetProcessor: Cancellable = Default.Cancellable
/** how long packets that are out of sequential order wait for the missing sequence before being expedited (ms) */ /** how long packets that are out of sequential order wait for the missing sequence before being expedited (ms) */
val inReorderTimeout = Config.app.network.middleware.inReorderTimeout val inReorderTimeout = Config.app.network.middleware.inReorderTimeout
/** Timer that handles the bundling and throttling of outgoing packets requesting packets with known subslot numbers */ /** Timer that handles the bundling and throttling of outgoing packets requesting packets with known subslot numbers */
var subslotMissingProcessor: Cancellable = Default.Cancellable var subslotMissingProcessor: Cancellable = Default.Cancellable
/** how long to wait between repeated requests for packets with known missing subslot numbers (ms) */ /** how long to wait between repeated requests for packets with known missing subslot numbers (ms) */
val inSubslotMissingDelay = Config.app.network.middleware.inSubslotMissingDelay val inSubslotMissingDelay = Config.app.network.middleware.inSubslotMissingDelay
/** how many time to repeat the request for a packet with a known missing subslot number */ /** how many time to repeat the request for a packet with a known missing subslot number */
val inSubslotMissingNumberOfAttempts = Config.app.network.middleware.inSubslotMissingAttempts val inSubslotMissingNumberOfAttempts = Config.app.network.middleware.inSubslotMissingAttempts
@ -274,6 +285,13 @@ class MiddlewareActor(
send(ServerStart(nonce, serverNonce), None, None) send(ServerStart(nonce, serverNonce), None, None)
cryptoSetup() cryptoSetup()
/** Unknown30 is used to reuse an existing crypto session when switching from login to world
* When not handling it, it appears that the client will fall back to using ClientStart
* TODO implement this
*/
case (Unknown30(nonce), _) =>
connectionClose()
// TODO ResetSequence // TODO ResetSequence
case _ => case _ =>
log.warn(s"Unexpected packet type $packet in start (before crypto)") log.warn(s"Unexpected packet type $packet in start (before crypto)")
@ -356,17 +374,17 @@ class MiddlewareActor(
packet match { packet match {
case (ClientFinished(clientPubKey, _), Some(_)) => case (ClientFinished(clientPubKey, _), Some(_)) =>
serverMACBuffer ++= msg.drop(3) serverMACBuffer ++= msg.drop(3)
val agreedKey = dh.agree(clientPubKey.toArray) val agreedKey = dh.agree(clientPubKey.toArray)
val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++ val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++
hex"00000000" ++ serverChallenge ++ hex"00000000" hex"00000000" ++ serverChallenge ++ hex"00000000"
val masterSecret = new Md5Mac(ByteVector.view(agreedKey)).updateFinal(agreedMessage) val masterSecret = new Md5Mac(ByteVector.view(agreedKey)).updateFinal(agreedMessage)
val mac = new Md5Mac(masterSecret) val mac = new Md5Mac(masterSecret)
//TODO verify client challenge? //TODO verify client challenge?
val serverChallengeResult = mac val serverChallengeResult = mac
.updateFinal(ByteVector("server finished".getBytes) ++ serverMACBuffer ++ hex"01", 0xc) .updateFinal(ByteVector("server finished".getBytes) ++ serverMACBuffer ++ hex"01", 0xc)
val encExpansion = ByteVector.view("server expansion".getBytes) ++ hex"0000" ++ serverChallenge ++ val encExpansion = ByteVector.view("server expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
hex"00000000" ++ clientChallenge ++ hex"00000000" hex"00000000" ++ clientChallenge ++ hex"00000000"
val decExpansion = ByteVector.view("client expansion".getBytes) ++ hex"0000" ++ serverChallenge ++ val decExpansion = ByteVector.view("client expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
hex"00000000" ++ clientChallenge ++ hex"00000000" hex"00000000" ++ clientChallenge ++ hex"00000000"
val expandedEncKey = mac.updateFinal(encExpansion, 64) val expandedEncKey = mac.updateFinal(encExpansion, 64)
val expandedDecKey = mac.updateFinal(decExpansion, 64) val expandedDecKey = mac.updateFinal(decExpansion, 64)
@ -381,13 +399,12 @@ class MiddlewareActor(
) )
send(ServerFinished(serverChallengeResult)) send(ServerFinished(serverChallengeResult))
//start the queue processor loop //start the queue processor loop
packetProcessor = packetProcessor = context.system.scheduler.scheduleWithFixedDelay(
context.system.scheduler.scheduleWithFixedDelay( packetProcessorDelay,
packetProcessorDelay, packetProcessorDelay
packetProcessorDelay )(() => {
)(()=> { context.self ! ProcessQueue()
context.self ! ProcessQueue() })
})
active() active()
case other => case other =>
@ -416,7 +433,7 @@ class MiddlewareActor(
activeSequenceFunc(packet, sequence) activeSequenceFunc(packet, sequence)
case Successful((packet, None)) => case Successful((packet, None)) =>
in(packet) in(packet)
case Failure(e) => case Failure(e) =>
log.error(s"Could not decode $connectionId's packet: $e") log.error(s"Could not decode $connectionId's packet: $e")
} }
Behaviors.same Behaviors.same
@ -446,7 +463,7 @@ class MiddlewareActor(
val onSignal: PartialFunction[(ActorContext[Command], Signal), Behavior[Command]] = { val onSignal: PartialFunction[(ActorContext[Command], Signal), Behavior[Command]] = {
case (_, PostStop) => case (_, PostStop) =>
context.stop(nextActor) context.stop(nextActor)
if(timesInReorderQueue > 0 || timesSubslotMissing > 0) { if (timesInReorderQueue > 0 || timesSubslotMissing > 0) {
log.trace(s"out of sequence checks: $timesInReorderQueue, subslot missing checks: $timesSubslotMissing") log.trace(s"out of sequence checks: $timesInReorderQueue, subslot missing checks: $timesSubslotMissing")
} }
packetProcessor.cancel() packetProcessor.cancel()
@ -575,31 +592,29 @@ class MiddlewareActor(
} else if (outQueue.nonEmpty) { } else if (outQueue.nonEmpty) {
val bundle = { val bundle = {
var length = 0L var length = 0L
val (_, bundle) = outQueue val (_, bundle) = outQueue.dequeueWhile {
.dequeueWhile { case (packet, payload) =>
case (packet, payload) => // packet length + MultiPacketEx header length
// packet length + MultiPacketEx header length val packetLength = payload.length + (
val packetLength = payload.length + ( if (payload.length < 2048) { 8L } //256 * 8; 1L * 8
if (payload.length < 2048) { 8L } //256 * 8; 1L * 8 else if (payload.length < 524288) { 16L } //65536 * 8; 2L * 8
else if (payload.length < 524288) { 16L } //65536 * 8; 2L * 8 else { 32L } //4L * 8
else { 32L } //4L * 8 )
) length += packetLength
length += packetLength
if (packetsBundledByThemselves.exists { _(packet) }) { if (packetsBundledByThemselves.exists { _(packet) }) {
if (length == packetLength) { if (length == packetLength) {
length += MTU length += MTU
true //dequeue only packet true //dequeue only packet
} else {
false //dequeue later
}
} else { } else {
// Some packets may be larger than the MTU limit, in that case we dequeue anyway and split later false //dequeue later
// We deduct some bytes to leave room for SlottedMetaPacket (4 bytes) and MultiPacketEx (2 bytes + prefix per packet)
length == packetLength || length <= (MTU - 6) * 8
} }
} } else {
.unzip // Some packets may be larger than the MTU limit, in that case we dequeue anyway and split later
// We deduct some bytes to leave room for SlottedMetaPacket (4 bytes) and MultiPacketEx (2 bytes + prefix per packet)
length == packetLength || length <= (MTU - 6) * 8
}
}.unzip
bundle bundle
} }
@ -616,7 +631,7 @@ class MiddlewareActor(
case Successful(data) => case Successful(data) =>
outQueueBundled.enqueue(smp(slot = 0, data.bytes)) outQueueBundled.enqueue(smp(slot = 0, data.bytes))
sendFirstBundle() sendFirstBundle()
case Failure(cause) => case Failure(cause) =>
log.error(s"could not bundle $bundle: ${cause.message}") log.error(s"could not bundle $bundle: ${cause.message}")
//to avoid packets being lost, unwrap bundle and queue the packets individually //to avoid packets being lost, unwrap bundle and queue the packets individually
bundle.foreach { packet => bundle.foreach { packet =>
@ -636,7 +651,7 @@ class MiddlewareActor(
* @see `activeNormal` * @see `activeNormal`
* @see `activeWithReordering` * @see `activeWithReordering`
*/ */
private var activeSequenceFunc: (PlanetSidePacket, Int)=>Unit = activeNormal private var activeSequenceFunc: (PlanetSidePacket, Int) => Unit = activeNormal
/** /**
* Properly handle the newly-arrived packet based on its sequence number. * Properly handle the newly-arrived packet based on its sequence number.
@ -651,7 +666,7 @@ class MiddlewareActor(
if (sequence == inSequence + 1) { if (sequence == inSequence + 1) {
inSequence = sequence inSequence = sequence
in(packet) in(packet)
} else if(sequence < inSequence) { //expedite this packet } else if (sequence < inSequence) { //expedite this packet
in(packet) in(packet)
} else if (sequence == inSequence) { } else if (sequence == inSequence) {
//do nothing? //do nothing?
@ -681,7 +696,7 @@ class MiddlewareActor(
inSequence = sequence inSequence = sequence
in(packet) in(packet)
processInReorderQueue() processInReorderQueue()
} else if(sequence < inSequence) { //expedite this packet } else if (sequence < inSequence) { //expedite this packet
inReorderQueue.filterInPlace(_.sequence == sequence) inReorderQueue.filterInPlace(_.sequence == sequence)
in(packet) in(packet)
inReorderQueueFunc = inReorderQueueTest inReorderQueueFunc = inReorderQueueTest
@ -690,7 +705,7 @@ class MiddlewareActor(
//do nothing? //do nothing?
} else { } else {
var insertAtIndex = 0 var insertAtIndex = 0
val length = inReorderQueue.length val length = inReorderQueue.length
while (insertAtIndex < length && sequence >= inReorderQueue(insertAtIndex).sequence) { while (insertAtIndex < length && sequence >= inReorderQueue(insertAtIndex).sequence) {
insertAtIndex += 1 insertAtIndex += 1
} }
@ -704,7 +719,7 @@ class MiddlewareActor(
* @see `inReorderQueueTest` * @see `inReorderQueueTest`
* @see `processInReorderQueueTimeoutOnly` * @see `processInReorderQueueTimeoutOnly`
*/ */
private var inReorderQueueFunc: ()=>Unit = doNothing private var inReorderQueueFunc: () => Unit = doNothing
/** /**
* Examine inbound packets that need to be reordered by sequence number and * Examine inbound packets that need to be reordered by sequence number and
@ -715,9 +730,9 @@ class MiddlewareActor(
*/ */
def processInReorderQueue(): Unit = { def processInReorderQueue(): Unit = {
timesInReorderQueue += 1 timesInReorderQueue += 1
var currentSequence = inSequence var currentSequence = inSequence
val currentTime = System.currentTimeMillis() val currentTime = System.currentTimeMillis()
val takenPackets = (inReorderQueue.indexWhere { currentTime - _.time > inReorderTimeout.toMillis } match { val takenPackets = (inReorderQueue.indexWhere { currentTime - _.time > inReorderTimeout.toMillis } match {
case -1 => case -1 =>
inReorderQueue inReorderQueue
.takeWhile { entry => .takeWhile { entry =>
@ -731,7 +746,7 @@ class MiddlewareActor(
} }
case index => case index =>
// Forward all packets ahead of any packet that has been in the queue for 50ms // Forward all packets ahead of any packet that has been in the queue for 50ms
val entries = inReorderQueue.take(index + 1) val entries = inReorderQueue.take(index + 1)
currentSequence = entries.last.sequence currentSequence = entries.last.sequence
entries entries
}).map(_.packet) }).map(_.packet)
@ -757,7 +772,7 @@ class MiddlewareActor(
inReorderQueue.dropInPlace(takenPackets.length) inReorderQueue.dropInPlace(takenPackets.length)
takenPackets.foreach { p => takenPackets.foreach { p =>
inReorderQueueFunc = inReorderQueueTest inReorderQueueFunc = inReorderQueueTest
inSequence = p.sequence inSequence = p.sequence
in(p.packet) in(p.packet)
} }
} }
@ -783,7 +798,7 @@ class MiddlewareActor(
* @see `inSubslotNotMissing` * @see `inSubslotNotMissing`
* @see `inSubslotMissingRequests` * @see `inSubslotMissingRequests`
*/ */
private var activeSubslotsFunc: (Int, Int, ByteVector)=>Unit = inSubslotNotMissing private var activeSubslotsFunc: (Int, Int, ByteVector) => Unit = inSubslotNotMissing
/** /**
* What to do with a `SlottedMetaPacket` control packet normally. * What to do with a `SlottedMetaPacket` control packet normally.
@ -851,7 +866,7 @@ class MiddlewareActor(
* resume normal operations when acting upon inbound `SlottedMetaPacket` packets. * resume normal operations when acting upon inbound `SlottedMetaPacket` packets.
* @param slot the optional slot to report the "first" `RelatedB` in a "while" * @param slot the optional slot to report the "first" `RelatedB` in a "while"
*/ */
def inSubslotsMissingRequestsFinished(slot: Int = 0) : Unit = { def inSubslotsMissingRequestsFinished(slot: Int = 0): Unit = {
if (inSubslotsMissing.isEmpty) { if (inSubslotsMissing.isEmpty) {
subslotMissingProcessor.cancel() subslotMissingProcessor.cancel()
activeSubslotsFunc = inSubslotNotMissing activeSubslotsFunc = inSubslotNotMissing
@ -870,25 +885,25 @@ class MiddlewareActor(
*/ */
def askForMissingSubslots(): Unit = { def askForMissingSubslots(): Unit = {
if (subslotMissingProcessor.isCancelled) { if (subslotMissingProcessor.isCancelled) {
subslotMissingProcessor = subslotMissingProcessor = context.system.scheduler.scheduleWithFixedDelay(
context.system.scheduler.scheduleWithFixedDelay( initialDelay = 0.milliseconds,
initialDelay = 0.milliseconds, inSubslotMissingDelay
inSubslotMissingDelay )(() => {
)(()=> { inSubslotsMissing.synchronized {
inSubslotsMissing.synchronized { timesSubslotMissing += inSubslotsMissing.size
timesSubslotMissing += inSubslotsMissing.size inSubslotsMissing.foreach {
inSubslotsMissing.foreach { case (subslot, attempt) => case (subslot, attempt) =>
val value = attempt - 1 val value = attempt - 1
if(value > 0) { if (value > 0) {
inSubslotsMissing(subslot) = value inSubslotsMissing(subslot) = value
} else { } else {
inSubslotsMissing.remove(subslot) inSubslotsMissing.remove(subslot)
} }
send(RelatedA(0, subslot)) send(RelatedA(0, subslot))
}
inSubslotsMissingRequestsFinished()
} }
}) inSubslotsMissingRequestsFinished()
}
})
} }
} }

View file

@ -12,20 +12,41 @@ object ControlPacketOpcode extends Enumeration {
type Type = Value type Type = Value
val val
// OPCODES 0x00-0f // OPCODES 0x00-0f
HandleGamePacket, // a whoopsi case: not actually a control packet, but a game packet HandleGamePacket, // a whoopsi case: not actually a control packet, but a game packet
ClientStart, // first packet ever sent during client connection ClientStart, // first packet ever sent during client connection
ServerStart, // second packet sent in response to ClientStart ServerStart, // second packet sent in response to ClientStart
MultiPacket, // used to send multiple packets with one UDP message (subpackets limited to <= 255) MultiPacket, // used to send multiple packets with one UDP message (subpackets limited to <= 255)
Unknown4, TeardownConnection, Unknown6, ControlSync, // sent to the server from the client Unknown4, //
TeardownConnection, //
Unknown6, //
ControlSync, // sent to the server from the client
// 0x08 // 0x08
ControlSyncResp, // the response generated by the server ControlSyncResp, // the response generated by the server
SlottedMetaPacket0, SlottedMetaPacket1, SlottedMetaPacket2, SlottedMetaPacket3, SlottedMetaPacket4, SlottedMetaPacket0, //
SlottedMetaPacket5, SlottedMetaPacket6, SlottedMetaPacket1, //
SlottedMetaPacket2, //
SlottedMetaPacket3, //
SlottedMetaPacket4, //
SlottedMetaPacket5, //
SlottedMetaPacket6, //
// OPCODES 0x10-1f // OPCODES 0x10-1f
SlottedMetaPacket7, RelatedA0, RelatedA1, RelatedA2, RelatedA3, RelatedB0, RelatedB1, RelatedB2, SlottedMetaPacket7, //
RelatedA0, //
RelatedA1, //
RelatedA2, //
RelatedA3, //
RelatedB0, //
RelatedB1, //
RelatedB2, //
// 0x18 // 0x18
RelatedB3, MultiPacketEx, // same as MultiPacket, but with the ability to send extended length packets RelatedB3, //
Unknown26, Unknown27, Unknown28, ConnectionClose, Unknown30 = Value MultiPacketEx, // same as MultiPacket, but with the ability to send extended length packets
Unknown26, //
Unknown27, //
Unknown28, //
ConnectionClose, //
Unknown30 // Probably a more lightweight variant of ClientStart, containing only the client nonce
= Value
private def noDecoder(opcode: ControlPacketOpcode.Type) = private def noDecoder(opcode: ControlPacketOpcode.Type) =
(bits: BitVector) => Attempt.failure(Err(s"Could not find a marshaller for control packet $opcode (${bits.toHex})")) (bits: BitVector) => Attempt.failure(Err(s"Could not find a marshaller for control packet $opcode (${bits.toHex})"))
@ -69,7 +90,7 @@ object ControlPacketOpcode extends Enumeration {
case 0x1b => noDecoder(Unknown27) case 0x1b => noDecoder(Unknown27)
case 0x1c => noDecoder(Unknown28) case 0x1c => noDecoder(Unknown28)
case 0x1d => control.ConnectionClose.decode case 0x1d => control.ConnectionClose.decode
case 0x1e => noDecoder(Unknown30) case 0x1e => control.Unknown30.decode
case _ => noDecoder(opcode) case _ => noDecoder(opcode)
} }

View file

@ -63,7 +63,7 @@ object PacketType extends Enumeration(1) {
} }
/** PlanetSide packet flags (beginning of most packets) */ /** PlanetSide packet flags (beginning of most packets) */
final case class PlanetSidePacketFlags(packetType: PacketType.Value, secured: Boolean) final case class PlanetSidePacketFlags(packetType: PacketType.Value, secured: Boolean, advanced: Boolean = true)
/** Codec for [[PlanetSidePacketFlags]] */ /** Codec for [[PlanetSidePacketFlags]] */
object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] { object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
@ -71,7 +71,8 @@ object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
("packet_type" | PacketType.codec) :: // first 4-bits ("packet_type" | PacketType.codec) :: // first 4-bits
("unused" | constant(bin"0")) :: ("unused" | constant(bin"0")) ::
("secured" | bool) :: ("secured" | bool) ::
("advanced" | constant(bin"1")) :: // we only support "advanced packets" //("advanced" | constant(bin"1")) :: // we only support "advanced packets"
("advanced" | bool) ::
("length_specified" | constant(bin"0")) // we DO NOT support this field ("length_specified" | constant(bin"0")) // we DO NOT support this field
).as[PlanetSidePacketFlags] ).as[PlanetSidePacketFlags]
} }

View file

@ -42,7 +42,7 @@ object PacketCoding {
case Some(_sequence) => case Some(_sequence) =>
uint16L.encode(_sequence) match { uint16L.encode(_sequence) match {
case Successful(_seq) => _seq case Successful(_seq) => _seq
case f @ Failure(_) => return f case f @ Failure(_) => return f
} }
case None => case None =>
return Failure(Err(s"Missing sequence")) return Failure(Err(s"Missing sequence"))
@ -174,7 +174,7 @@ object PacketCoding {
): Attempt[(PlanetSidePacket, Int)] = { ): Attempt[(PlanetSidePacket, Int)] = {
val (flags, remainder) = Codec.decode[PlanetSidePacketFlags](BitVector(msg)) match { val (flags, remainder) = Codec.decode[PlanetSidePacketFlags](BitVector(msg)) match {
case Successful(DecodeResult(value, _remainder)) => (value, _remainder) case Successful(DecodeResult(value, _remainder)) => (value, _remainder)
case Failure(e) => return Failure(Err(s"Failed to parse packet flags: ${e.message}")) case Failure(e) => return Failure(Err(s"Failed to parse packet flags: ${e.message}"))
} }
flags.packetType match { flags.packetType match {
@ -218,8 +218,8 @@ object PacketCoding {
case (PacketType.ResetSequence, Some(_crypto)) => case (PacketType.ResetSequence, Some(_crypto)) =>
_crypto.decrypt(payload.drop(1)) match { _crypto.decrypt(payload.drop(1)) match {
case Successful(p) if p == hex"01" => Successful((ResetSequence(), sequence)) case Successful(p) if p == hex"01" => Successful((ResetSequence(), sequence))
case Successful(p) => Failure(Err(s"ResetSequence decrypted to unsupported value - $p")) case Successful(p) => Failure(Err(s"ResetSequence decrypted to unsupported value - $p"))
case _ => Failure(Err(s"ResetSequence did not decrypt properly")) case _ => Failure(Err(s"ResetSequence did not decrypt properly"))
} }
case (ptype, _) => case (ptype, _) =>
Failure(Err(s"Cannot unmarshal $ptype packet at all")) Failure(Err(s"Cannot unmarshal $ptype packet at all"))
@ -238,8 +238,7 @@ object PacketCoding {
def decodePacket(msg: ByteVector): Attempt[PlanetSidePacket] = { def decodePacket(msg: ByteVector): Attempt[PlanetSidePacket] = {
if (msg.length < PLANETSIDE_MIN_PACKET_SIZE) if (msg.length < PLANETSIDE_MIN_PACKET_SIZE)
return Failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes")) return Failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
val firstByte = msg { 0 } msg(0) match {
firstByte match {
case 0x00 => case 0x00 =>
// control packets don't need the first byte // control packets don't need the first byte
ControlPacketOpcode.codec.decode(msg.drop(1).bits) match { ControlPacketOpcode.codec.decode(msg.drop(1).bits) match {
@ -303,7 +302,7 @@ object PacketCoding {
} }
} catch { } catch {
case e: Throwable => case e: Throwable =>
val msg = if(e.getMessage == null) e.getClass.getSimpleName else e.getMessage val msg = if (e.getMessage == null) e.getClass.getSimpleName else e.getMessage
Failure(Err(s"encrypt error: '$msg' data: ${packetWithPadding.toHex}")) Failure(Err(s"encrypt error: '$msg' data: ${packetWithPadding.toHex}"))
} }
} }
@ -321,14 +320,14 @@ object PacketCoding {
// last byte is the padding length // last byte is the padding length
val padding = uint8L.decode(payloadDecrypted.takeRight(1).bits) match { val padding = uint8L.decode(payloadDecrypted.takeRight(1).bits) match {
case Successful(_padding) => _padding.value case Successful(_padding) => _padding.value
case Failure(e) => return Failure(Err(s"Failed to decode the encrypted padding length: ${e.message}")) case Failure(e) => return Failure(Err(s"Failed to decode the encrypted padding length: ${e.message}"))
} }
val payloadNoPadding = payloadDecrypted.dropRight(1 + padding) val payloadNoPadding = payloadDecrypted.dropRight(1 + padding)
val payloadMac = payloadNoPadding.takeRight(Md5Mac.MACLENGTH) val payloadMac = payloadNoPadding.takeRight(Md5Mac.MACLENGTH)
val mac = bytes(Md5Mac.MACLENGTH).decode(payloadMac.bits) match { val mac = bytes(Md5Mac.MACLENGTH).decode(payloadMac.bits) match {
case Failure(e) => return Failure(Err("Failed to extract the encrypted MAC: " + e.message)) case Failure(e) => return Failure(Err("Failed to extract the encrypted MAC: " + e.message))
case Successful(_mac) => _mac.value case Successful(_mac) => _mac.value
} }

View file

@ -0,0 +1,18 @@
package net.psforever.packet.control
import net.psforever.packet.{ControlPacketOpcode, Marshallable, PlanetSideControlPacket}
import scodec.Codec
import scodec.bits._
import scodec.codecs._
final case class Unknown30(clientNonce: Long) extends PlanetSideControlPacket {
type Packet = Unknown30
def opcode = ControlPacketOpcode.Unknown30
def encode = Unknown30.encode(this)
}
object Unknown30 extends Marshallable[Unknown30] {
implicit val codec: Codec[Unknown30] = (
("client_nonce" | uint32L)
).as[Unknown30]
}