Fix thread safety issues

* Synchronize RC5 cipher access
* Replace avatar using message to self in AvatarActor
This commit is contained in:
Jakob Gillich 2020-12-31 04:20:49 +01:00
parent 2f0c6d98fd
commit 4c1b9a64d3
No known key found for this signature in database
GPG key ID: FD8BF52DB8452C91
6 changed files with 25 additions and 25 deletions

1
.gitignore vendored
View file

@ -13,6 +13,7 @@ out/
project/metals.sbt project/metals.sbt
/docs /docs
.vscode .vscode
.bsp
# User configs # User configs
config/psforever.conf config/psforever.conf

View file

@ -1 +1 @@
sbt.version = 1.3.13 sbt.version = 1.4.5

View file

@ -166,6 +166,7 @@ class MiddlewareActor(
val queueProcessor: Cancellable = { val queueProcessor: Cancellable = {
context.system.scheduler.scheduleWithFixedDelay(10.milliseconds, 10.milliseconds)(() => { context.system.scheduler.scheduleWithFixedDelay(10.milliseconds, 10.milliseconds)(() => {
try { try {
if (outQueue.nonEmpty && outQueueBundled.isEmpty) { if (outQueue.nonEmpty && outQueueBundled.isEmpty) {
var length = 0L var length = 0L
val bundle = outQueue val bundle = outQueue

View file

@ -407,6 +407,10 @@ class AvatarActor(
case ReplaceAvatar(newAvatar) => case ReplaceAvatar(newAvatar) =>
avatar = newAvatar avatar = newAvatar
avatar.deployables.UpdateMaxCounts(avatar.certifications)
updateDeployableUIElements(
avatar.deployables.UpdateUI()
)
Behaviors.same Behaviors.same
case AddFirstTimeEvent(event) => case AddFirstTimeEvent(event) =>
@ -461,15 +465,12 @@ class AvatarActor(
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
PlanetsideAttributeMessage(session.get.player.GUID, 24, certification.value) PlanetsideAttributeMessage(session.get.player.GUID, 24, certification.value)
) )
avatar = avatar.copy(certifications = avatar.certifications.diff(replace) + certification) context.self ! ReplaceAvatar(
avatar.copy(certifications = avatar.certifications.diff(replace) + certification)
)
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true) ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true)
) )
avatar.deployables.UpdateMaxCounts(avatar.certifications)
updateDeployableUIElements(
avatar.deployables.UpdateUI()
)
} }
} }
@ -510,19 +511,15 @@ class AvatarActor(
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = false) ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = false)
) )
case Success(certs) => case Success(certs) =>
context.self ! ReplaceAvatar(avatar.copy(certifications = avatar.certifications.diff(remove)))
certs.foreach { cert => certs.foreach { cert =>
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
PlanetsideAttributeMessage(session.get.player.GUID, 25, cert.value) PlanetsideAttributeMessage(session.get.player.GUID, 25, cert.value)
) )
} }
avatar = avatar.copy(certifications = avatar.certifications.diff(remove))
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true) ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true)
) )
avatar.deployables.UpdateMaxCounts(avatar.certifications)
updateDeployableUIElements(
avatar.deployables.UpdateUI()
)
} }
} }
Behaviors.same Behaviors.same
@ -560,11 +557,7 @@ class AvatarActor(
) )
.onComplete { .onComplete {
case Success(_) => case Success(_) =>
avatar = avatar.copy(certifications = certifications) context.self ! ReplaceAvatar(avatar.copy(certifications = certifications))
avatar.deployables.UpdateMaxCounts(avatar.certifications)
updateDeployableUIElements(
avatar.deployables.UpdateUI()
)
case Failure(exception) => case Failure(exception) =>
log.error(exception)("db failure") log.error(exception)("db failure")
} }
@ -600,7 +593,9 @@ class AvatarActor(
.run(query[persistence.Implant].insert(_.name -> lift(definition.Name), _.avatarId -> lift(avatar.id))) .run(query[persistence.Implant].insert(_.name -> lift(definition.Name), _.avatarId -> lift(avatar.id)))
.onComplete { .onComplete {
case Success(_) => case Success(_) =>
avatar = avatar.copy(implants = avatar.implants.updated(index, Some(Implant(definition)))) context.self ! ReplaceAvatar(
avatar.copy(implants = avatar.implants.updated(index, Some(Implant(definition))))
)
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
AvatarImplantMessage( AvatarImplantMessage(
session.get.player.GUID, session.get.player.GUID,
@ -641,7 +636,7 @@ class AvatarActor(
) )
.onComplete { .onComplete {
case Success(_) => case Success(_) =>
avatar = avatar.copy(implants = avatar.implants.updated(index, None)) context.self ! ReplaceAvatar(avatar.copy(implants = avatar.implants.updated(index, None)))
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Remove, index, 0) AvatarImplantMessage(session.get.player.GUID, ImplantAction.Remove, index, 0)
) )
@ -668,7 +663,7 @@ class AvatarActor(
case Success(_) => case Success(_) =>
loadLoadouts().onComplete { loadLoadouts().onComplete {
case Success(loadouts) => case Success(loadouts) =>
avatar = avatar.copy(loadouts = loadouts) context.self ! ReplaceAvatar(avatar.copy(loadouts = loadouts))
context.self ! RefreshLoadouts() context.self ! RefreshLoadouts()
case Failure(exception) => log.error(exception)("db failure") case Failure(exception) => log.error(exception)("db failure")
} }
@ -694,7 +689,7 @@ class AvatarActor(
) )
.onComplete { .onComplete {
case Success(_) => case Success(_) =>
avatar = avatar.copy(loadouts = avatar.loadouts.updated(number, None)) context.self ! ReplaceAvatar(avatar.copy(loadouts = avatar.loadouts.updated(number, None)))
sessionActor ! SessionActor.SendResponse(FavoritesMessage(loadoutType, player.GUID, number, "")) sessionActor ! SessionActor.SendResponse(FavoritesMessage(loadoutType, player.GUID, number, ""))
case Failure(exception) => case Failure(exception) =>
log.error(exception)("db failure") log.error(exception)("db failure")

View file

@ -249,7 +249,6 @@ object PacketCoding {
macEncryptionKey: ByteVector, macEncryptionKey: ByteVector,
macDecryptionKey: ByteVector macDecryptionKey: ByteVector
) { ) {
private val iv = BigInt(64, random)
private val rc5Spec = new RC5ParameterSpec(0, 16, 32) private val rc5Spec = new RC5ParameterSpec(0, 16, 32)
private val rc5Encrypt = Cipher.getInstance("RC5/ECB/NoPadding") private val rc5Encrypt = Cipher.getInstance("RC5/ECB/NoPadding")
private val rc5Decrypt = Cipher.getInstance("RC5/ECB/NoPadding") private val rc5Decrypt = Cipher.getInstance("RC5/ECB/NoPadding")
@ -276,7 +275,9 @@ object PacketCoding {
val packetWithPadding = packetNoPadding ++ ByteVector.fill(paddingNeeded)(0x00) ++ paddingEncoded.toByteVector val packetWithPadding = packetNoPadding ++ ByteVector.fill(paddingNeeded)(0x00) ++ paddingEncoded.toByteVector
// raw packets plus MAC, padded to the nearest 8 byte boundary // raw packets plus MAC, padded to the nearest 8 byte boundary
try { try {
Successful(ByteVector.view(rc5Encrypt.doFinal(packetWithPadding.toArray))) rc5Encrypt.synchronized {
Successful(ByteVector.view(rc5Encrypt.doFinal(packetWithPadding.toArray)))
}
} catch { } catch {
case e: Throwable => Failure(Err(s"encrypt error: '${e.getMessage}' data: ${packetWithPadding.toHex}")) case e: Throwable => Failure(Err(s"encrypt error: '${e.getMessage}' data: ${packetWithPadding.toHex}"))
} }
@ -285,7 +286,9 @@ object PacketCoding {
def decrypt(data: ByteVector): Attempt[ByteVector] = { def decrypt(data: ByteVector): Attempt[ByteVector] = {
val payloadDecrypted = val payloadDecrypted =
try { try {
ByteVector.view(rc5Decrypt.doFinal(data.toArray)) rc5Decrypt.synchronized {
ByteVector.view(rc5Decrypt.doFinal(data.toArray))
}
} catch { } catch {
case e: Throwable => return Failure(Err(e.getMessage)) case e: Throwable => return Failure(Err(e.getMessage))
} }

View file

@ -57,7 +57,7 @@ class Md5Mac(val key: ByteVector) {
assert(key.length == KEYLENGTH, s"key length must be ${KEYLENGTH}, not ${key.length}") assert(key.length == KEYLENGTH, s"key length must be ${KEYLENGTH}, not ${key.length}")
doKey() doKey()
private def doKey() = { private def doKey(): Unit = {
val ek: ListBuffer[Byte] = ListBuffer.fill(48)(0) val ek: ListBuffer[Byte] = ListBuffer.fill(48)(0)
val data: ListBuffer[Byte] = ListBuffer.fill(128)(0) val data: ListBuffer[Byte] = ListBuffer.fill(128)(0)
(0 until 16).foreach(j => { (0 until 16).foreach(j => {