From 53488613d6b33c72d59577598926d2be656842de Mon Sep 17 00:00:00 2001 From: Chord Date: Sun, 24 Apr 2016 19:06:17 -0400 Subject: [PATCH] New session pipeline and able to see server selection --- .../src/main/scala/CryptoSessionActor.scala | 302 ++++++++++++++++++ pslogin/src/main/scala/LoginSession.scala | 12 - .../src/main/scala/LoginSessionActor.scala | 268 +++------------- pslogin/src/main/scala/SessionRouter.scala | 76 ++++- pslogin/src/main/scala/UdpListener.scala | 6 +- 5 files changed, 411 insertions(+), 253 deletions(-) create mode 100644 pslogin/src/main/scala/CryptoSessionActor.scala delete mode 100644 pslogin/src/main/scala/LoginSession.scala diff --git a/pslogin/src/main/scala/CryptoSessionActor.scala b/pslogin/src/main/scala/CryptoSessionActor.scala new file mode 100644 index 00000000..78080b3a --- /dev/null +++ b/pslogin/src/main/scala/CryptoSessionActor.scala @@ -0,0 +1,302 @@ +// Copyright (c) 2016 PSForever.net to present +import java.net.{InetAddress, InetSocketAddress} + +import akka.actor.{ActorRef, Identify, Actor, ActorLogging} +import psforever.crypto.CryptoInterface.{CryptoStateWithMAC, CryptoState} +import psforever.crypto.CryptoInterface +import psforever.net._ +import scodec.Attempt.{Successful, Failure} +import scodec.bits._ +import scodec.{Err, Attempt, Codec} +import scodec.codecs.{uint16L, uint8L, bytes} +import java.security.SecureRandom + +/** + * Actor that stores crypto state for a connection and filters away any packet metadata. + */ +class CryptoSessionActor extends Actor with ActorLogging { + var leftRef : ActorRef = ActorRef.noSender + var rightRef : ActorRef = ActorRef.noSender + + var cryptoDHState = new CryptoInterface.CryptoDHState() + var cryptoState : Option[CryptoInterface.CryptoStateWithMAC] = None + val random = new SecureRandom() + + // crypto handshake state + var serverChallenge = ByteVector.empty + var serverChallengeResult = ByteVector.empty + var serverMACBuffer = ByteVector.empty + + var clientPublicKey = ByteVector.empty + var clientChallenge = ByteVector.empty + var clientChallengeResult = ByteVector.empty + + def receive = Initializing + + def Initializing : Receive = { + case HelloFriend(right) => + leftRef = sender() + rightRef = right.asInstanceOf[ActorRef] + + // who ever we send to has to send something back to us + rightRef ! HelloFriend(self) + + context.become(NewClient) + case _ => + log.error("Unknown message") + context.stop(self) + } + + def NewClient : Receive = { + case RawPacket(msg) => + // PacketCoding.DecodePacket + PacketCoding.UnmarshalPacket(msg) match { + case Failure(e) => log.error("Could not decode packet: " + e) + case Successful(p) => + println("RECV: " + p) + + p match { + case ControlPacket(_, ClientStart(nonce)) => + sendResponse(PacketCoding.CreateControlPacket(ServerStart(nonce, Math.abs(random.nextInt())))) + + context.become(CryptoExchange) + case default => + log.error("Unexpected packet type " + p) + } + } + case default => log.error(s"Invalid message received ${default}") + } + + def CryptoExchange : Receive = { + case RawPacket(msg) => + PacketCoding.UnmarshalPacket(msg, CryptoPacketOpcode.ClientChallengeXchg) match { + case Failure(e) => log.error("Could not decode packet: " + e) + case Successful(p) => + println("RECV: " + p) + + p match { + case CryptoPacket(seq, ClientChallengeXchg(time, challenge, p, g)) => + // initialize our crypto state from the client's P and G + cryptoDHState.start(p, g) + + // save the client challenge + clientChallenge = ServerChallengeXchg.getCompleteChallenge(time, challenge) + + // save the packet we got for a MAC check later. drop the first 3 bytes + serverMACBuffer ++= msg.drop(3) + + val serverTime = System.currentTimeMillis() / 1000L + val randomChallenge = getRandBytes(0xc) + + // store the complete server challenge for later + serverChallenge = ServerChallengeXchg.getCompleteChallenge(serverTime, randomChallenge) + + val packet = PacketCoding.CreateCryptoPacket(seq, + ServerChallengeXchg(serverTime, randomChallenge, cryptoDHState.getPublicKey)) + + val sentPacket = sendResponse(packet) + + // save the sent packet a MAC check + serverMACBuffer ++= sentPacket.drop(3) + + context.become(CryptoSetupFinishing) + case default => log.error("Unexpected packet type " + p) + } + } + case default => log.error(s"Invalid message received ${default}") + } + + def CryptoSetupFinishing : Receive = { + case RawPacket(msg) => + PacketCoding.UnmarshalPacket(msg, CryptoPacketOpcode.ClientFinished) match { + case Failure(e) => log.error("Could not decode packet: " + e) + case Successful(p) => + println("RECV: " + p) + + p match { + case CryptoPacket(seq, ClientFinished(clientPubKey, clientChalResult)) => + clientPublicKey = clientPubKey + clientChallengeResult = clientChalResult + + // save the packet we got for a MAC check later + serverMACBuffer ++= msg.drop(3) + + val agreedValue = cryptoDHState.agree(clientPublicKey) + + /*println("Agreed: " + agreedValue) + println(s"Client challenge: $clientChallenge")*/ + val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++ + hex"00000000" ++ serverChallenge ++ hex"00000000" + + //println("In message: " + agreedMessage) + + val masterSecret = CryptoInterface.MD5MAC(agreedValue, + agreedMessage, + 20) + + //println("Master secret: " + masterSecret) + + serverChallengeResult = CryptoInterface.MD5MAC(masterSecret, + ByteVector("server finished".getBytes) ++ serverMACBuffer ++ hex"01", + 0xc) + + val clientChallengeResultCheck = CryptoInterface.MD5MAC(masterSecret, + ByteVector("client finished".getBytes) ++ serverMACBuffer ++ hex"01" ++ clientChallengeResult ++ hex"01", + 0xc) + + //println("Check result: " + CryptoInterface.verifyMAC(clientChallenge, clientChallengeResult)) + + val decExpansion = ByteVector("client expansion".getBytes) ++ hex"0000" ++ serverChallenge ++ + hex"00000000" ++ clientChallenge ++ hex"00000000" + + val encExpansion = ByteVector("server expansion".getBytes) ++ hex"0000" ++ serverChallenge ++ + hex"00000000" ++ clientChallenge ++ hex"00000000" + + /*println("DecExpansion: " + decExpansion) + println("EncExpansion: " + encExpansion)*/ + + // expand the encryption and decryption keys + // The first 20 bytes are for RC5, and the next 16 are for the MAC'ing keys + val expandedDecKey = CryptoInterface.MD5MAC(masterSecret, + decExpansion, + 0x40) // this is what is visible in IDA + + val expandedEncKey = CryptoInterface.MD5MAC(masterSecret, + encExpansion, + 0x40) + + val decKey = expandedDecKey.take(20) + val encKey = expandedEncKey.take(20) + val decMACKey = expandedDecKey.drop(20).take(16) + val encMACKey = expandedEncKey.drop(20).take(16) + + /*println("**** DecKey: " + decKey) + println("**** EncKey: " + encKey) + println("**** DecMacKey: " + decMACKey) + println("**** EncMacKey: " + encMACKey)*/ + + // spin up our encryption program + cryptoState = Some(new CryptoStateWithMAC(decKey, encKey, decMACKey, encMACKey)) + + val packet = PacketCoding.CreateCryptoPacket(seq, + ServerFinished(serverChallengeResult)) + + sendResponse(packet) + + context.become(Established) + case default => failWithError("Unexpected packet type " + default) + } + } + case default => failWithError(s"Invalid message received ${default}") + } + + def Established : Receive = { + case RawPacket(msg) => + PacketCoding.UnmarshalPacket(msg) match { + case Successful(p) => + p match { + case encPacket @ EncryptedPacket(seq, _) => + println("Decrypting packet..." + encPacket) + PacketCoding.decryptPacket(cryptoState.get, encPacket) match { + case Successful(packet) => + println("RECV[E]: " + packet) + + self ! packet + case Failure(e) => + println("Failed to decode encrypted packet: " + e) + } + case default => failWithError("Unexpected packet type " + default) + + } + case Failure(e) => println("Could not decode raw packet: " + e) + } + case ctrl @ ControlPacket(_, _) => + val from = sender() + + handleEstablishedPacket(from, ctrl) + case game @ GamePacket(_, _, _) => + val from = sender() + + handleEstablishedPacket(from, game) + /* case SlottedMetaPacket(innerPacket) => + PacketCoding.DecodePacket(innerPacket) match { + case Successful(p) => + println("RECV[INNER]: " + p) + + val packet = PacketCoding.encryptPacket(cryptoState.get, PacketCoding.CreateGamePacket(3, + LoginRespMessage("AAAABBBBCCCCDDDD", + hex"00000000 18FABE0C 00000000 00000000", + 0, 1, 2, 685276011, + "AAAAAAAA", 0, false + ))).require + + sendResponse(packet) + + val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", + Vector( + WorldInformation("PSForever", WorldStatus.Up, ServerType.Development, + Vector(WorldConnectionInfo(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 51001))), EmpireNeed.TR) + )) + + sendResponse(PacketCoding.encryptPacket(cryptoState.get, PacketCoding.CreateGamePacket(4, + msg + )).require) + case Failure(e) => println("Failed to decode inner packet " + e) + } + */ + case default => failWithError(s"Invalid message received ${default}") + } + + def failWithError(error : String) = { + log.error(error) + sendResponse(PacketCoding.CreateControlPacket(ConnectionClose())) + } + + def resetState() : Unit = { + context.become(receive) + + // reset the crypto primitives + cryptoDHState.close + cryptoDHState = new CryptoInterface.CryptoDHState() + + if(cryptoState.isDefined) { + cryptoState.get.close + cryptoState = None + } + + serverChallenge = ByteVector.empty + serverChallengeResult = ByteVector.empty + serverMACBuffer = ByteVector.empty + clientPublicKey = ByteVector.empty + clientChallenge = ByteVector.empty + clientChallengeResult = ByteVector.empty + } + + def handleEstablishedPacket(from : ActorRef, cont : PlanetSidePacketContainer) = { + // we are processing a packet we decrypted + if(from == self) { + rightRef ! cont + } else if(from == rightRef) { // processing a completed packet from the right. encrypt + val packet = PacketCoding.encryptPacket(cryptoState.get, cont).require + sendResponse(packet) + } else { + log.error("Invalid sender") + } + } + + def sendResponse(cont : PlanetSidePacketContainer) : ByteVector = { + println("CRYPTO SEND: " + cont) + val pkt = PacketCoding.MarshalPacket(cont).require + val bytes = pkt.toByteVector + + leftRef ! ResponsePacket(bytes) + + bytes + } + + def getRandBytes(amount : Int) : ByteVector = { + val array = Array.ofDim[Byte](amount) + random.nextBytes(array) + ByteVector.view(array) + } +} diff --git a/pslogin/src/main/scala/LoginSession.scala b/pslogin/src/main/scala/LoginSession.scala deleted file mode 100644 index 42be50b1..00000000 --- a/pslogin/src/main/scala/LoginSession.scala +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2016 PSForever.net to present -import java.net.InetSocketAddress - -import akka.actor.ActorRef -import scodec.bits.{BitVector, ByteVector} - -class LoginSession(id : Long, socket : ActorRef, address : InetSocketAddress) { - - def send(msg : BitVector) = { - socket ! SendPacket(msg.toByteVector, address) - } -} diff --git a/pslogin/src/main/scala/LoginSessionActor.scala b/pslogin/src/main/scala/LoginSessionActor.scala index d3b2872c..062f5972 100644 --- a/pslogin/src/main/scala/LoginSessionActor.scala +++ b/pslogin/src/main/scala/LoginSessionActor.scala @@ -1,225 +1,65 @@ // Copyright (c) 2016 PSForever.net to present -import akka.actor.{Actor, ActorLogging} -import psforever.crypto.CryptoInterface.{CryptoStateWithMAC, CryptoState} -import psforever.crypto.CryptoInterface +import java.net.{InetAddress, InetSocketAddress} + +import akka.actor.{ActorRef, Identify, Actor, ActorLogging} import psforever.net._ -import scodec.Attempt.{Successful, Failure} +import scodec.Attempt.{Failure, Successful} import scodec.bits._ -import scodec.{Err, Attempt, Codec} -import scodec.codecs.{uint16L, uint8L, bytes} -import java.security.SecureRandom -/** - * Actor that stores crypto state for a connection and filters away any packet metadata. - * Also decrypts and handles packet retries using the sequence numbers. - * @param session Per session state - */ -class LoginSessionActor(session : LoginSession) extends Actor with ActorLogging { - var cryptoDHState = new CryptoInterface.CryptoDHState() - var cryptoState : Option[CryptoInterface.CryptoStateWithMAC] = None - val random = new SecureRandom() +class LoginSessionActor extends Actor with ActorLogging { + var leftRef : ActorRef = ActorRef.noSender + var rightRef : ActorRef = ActorRef.noSender - // crypto handshake state - var serverChallenge = ByteVector.empty - var serverChallengeResult = ByteVector.empty - var serverMACBuffer = ByteVector.empty + def receive = Initializing - var clientPublicKey = ByteVector.empty - var clientChallenge = ByteVector.empty - var clientChallengeResult = ByteVector.empty + def Initializing : Receive = { + case HelloFriend(right) => + leftRef = sender() + rightRef = right.asInstanceOf[ActorRef] - def receive = NewClient - - def NewClient : Receive = { - case RawPacket(msg) => - // PacketCoding.DecodePacket - PacketCoding.UnmarshalPacket(msg) match { - case Failure(e) => log.error("Could not decode packet: " + e) - case Successful(p) => - println("RECV: " + p) - - p match { - case ControlPacket(_, ClientStart(nonce)) => - sendResponse(PacketCoding.CreateControlPacket(ServerStart(nonce, Math.abs(random.nextInt())))) - - context.become(CryptoExchange) - case default => - log.error("Unexpected packet type " + p) - } - } - case default => log.error(s"Invalid message received ${default}") + context.become(Started) + case _ => + log.error("Unknown message") + context.stop(self) } - def CryptoExchange : Receive = { - case RawPacket(msg) => - PacketCoding.UnmarshalPacket(msg, CryptoPacketOpcode.ClientChallengeXchg) match { - case Failure(e) => log.error("Could not decode packet: " + e) - case Successful(p) => - println("RECV: " + p) - - p match { - case CryptoPacket(seq, ClientChallengeXchg(time, challenge, p, g)) => - // initialize our crypto state from the client's P and G - cryptoDHState.start(p, g) - - // save the client challenge - clientChallenge = ServerChallengeXchg.getCompleteChallenge(time, challenge) - - // save the packet we got for a MAC check later. drop the first 3 bytes - serverMACBuffer ++= msg.drop(3) - - val serverTime = System.currentTimeMillis() / 1000L - val randomChallenge = getRandBytes(0xc) - - // store the complete server challenge for later - serverChallenge = ServerChallengeXchg.getCompleteChallenge(serverTime, randomChallenge) - - val packet = PacketCoding.CreateCryptoPacket(seq, - ServerChallengeXchg(serverTime, randomChallenge, cryptoDHState.getPublicKey)) - - val sentPacket = sendResponse(packet) - - // save the sent packet a MAC check - serverMACBuffer ++= sentPacket.drop(3) - - context.become(CryptoSetupFinishing) - case default => log.error("Unexpected packet type " + p) - } - } - case default => log.error(s"Invalid message received ${default}") + def Started : Receive = { + case ctrl @ ControlPacket(opcode, pkt) => + handleControlPkt(pkt) + case game @ GamePacket(opcode, seq, pkt) => + handleGamePkt(pkt) + case default => failWithError(s"Invalid message received $default") } - def CryptoSetupFinishing : Receive = { - case RawPacket(msg) => - PacketCoding.UnmarshalPacket(msg, CryptoPacketOpcode.ClientFinished) match { - case Failure(e) => log.error("Could not decode packet: " + e) - case Successful(p) => - println("RECV: " + p) - - p match { - case CryptoPacket(seq, ClientFinished(clientPubKey, clientChalResult)) => - clientPublicKey = clientPubKey - clientChallengeResult = clientChalResult - - // save the packet we got for a MAC check later - serverMACBuffer ++= msg.drop(3) - - val agreedValue = cryptoDHState.agree(clientPublicKey) - - /*println("Agreed: " + agreedValue) - println(s"Client challenge: $clientChallenge")*/ - val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++ - hex"00000000" ++ serverChallenge ++ hex"00000000" - - //println("In message: " + agreedMessage) - - val masterSecret = CryptoInterface.MD5MAC(agreedValue, - agreedMessage, - 20) - - //println("Master secret: " + masterSecret) - - serverChallengeResult = CryptoInterface.MD5MAC(masterSecret, - ByteVector("server finished".getBytes) ++ serverMACBuffer ++ hex"01", - 0xc) - - val clientChallengeResultCheck = CryptoInterface.MD5MAC(masterSecret, - ByteVector("client finished".getBytes) ++ serverMACBuffer ++ hex"01" ++ clientChallengeResult ++ hex"01", - 0xc) - - //println("Check result: " + CryptoInterface.verifyMAC(clientChallenge, clientChallengeResult)) - - val decExpansion = ByteVector("client expansion".getBytes) ++ hex"0000" ++ serverChallenge ++ - hex"00000000" ++ clientChallenge ++ hex"00000000" - - val encExpansion = ByteVector("server expansion".getBytes) ++ hex"0000" ++ serverChallenge ++ - hex"00000000" ++ clientChallenge ++ hex"00000000" - - /*println("DecExpansion: " + decExpansion) - println("EncExpansion: " + encExpansion)*/ - - // expand the encryption and decryption keys - // The first 20 bytes are for RC5, and the next 16 are for the MAC'ing keys - val expandedDecKey = CryptoInterface.MD5MAC(masterSecret, - decExpansion, - 0x40) // this is what is visible in IDA - - val expandedEncKey = CryptoInterface.MD5MAC(masterSecret, - encExpansion, - 0x40) - - val decKey = expandedDecKey.take(20) - val encKey = expandedEncKey.take(20) - val decMACKey = expandedDecKey.drop(20).take(16) - val encMACKey = expandedEncKey.drop(20).take(16) - - /*println("**** DecKey: " + decKey) - println("**** EncKey: " + encKey) - println("**** DecMacKey: " + decMACKey) - println("**** EncMacKey: " + encMACKey)*/ - - // spin up our encryption program - cryptoState = Some(new CryptoStateWithMAC(decKey, encKey, decMACKey, encMACKey)) - - val packet = PacketCoding.CreateCryptoPacket(seq, - ServerFinished(serverChallengeResult)) - - sendResponse(packet) - - context.become(Established) - case default => failWithError("Unexpected packet type " + default) - } - } - case default => failWithError(s"Invalid message received ${default}") - } - - def Established : Receive = { - case RawPacket(msg) => - PacketCoding.UnmarshalPacket(msg) match { - case Successful(p) => - p match { - case encPacket @ EncryptedPacket(seq, _) => - println("Decrypting packet..." + encPacket) - PacketCoding.decryptPacket(cryptoState.get, encPacket) match { - case Successful(packet) => - println("RECV[E]: " + packet) - - self ! packet - case Failure(e) => - println("Failed to decode encrypted packet: " + e) - } - case default => failWithError("Unexpected packet type " + default) - - } - case Failure(e) => println("Could not decode raw packet: " + e) - } - case ctrl @ ControlPacket(_, pkt) => pkt match { + def handleControlPkt(pkt : PlanetSideControlPacket) = { + pkt match { case SlottedMetaPacket(innerPacket) => PacketCoding.DecodePacket(innerPacket) match { case Successful(p) => println("RECV[INNER]: " + p) - val packet = PacketCoding.encryptPacket(cryptoState.get, PacketCoding.CreateGamePacket(3, - LoginRespMessage("AAAABBBBCCCCDDDD", - hex"00000000 18FABE0C 00000000 00000000", - 0, 1, 2, 685276011, - "AAAAAAAA", 0, false - ))).require + val packet = LoginRespMessage("AAAABBBBCCCCDDDD", + hex"00000000 18FABE0C 00000000 00000000", + 0, 1, 2, 685276011, + "AAAAAAAA", 0, false + ) - sendResponse(packet) + sendResponse(PacketCoding.CreateGamePacket(0, packet)) val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", Vector( - WorldInformation("gemini", WorldStatus.Up, ServerType.Released, Vector(), EmpireNeed.NC) + WorldInformation("PSForever", WorldStatus.Up, ServerType.Development, + Vector(WorldConnectionInfo(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 51001))), EmpireNeed.TR) )) - sendResponse(PacketCoding.encryptPacket(cryptoState.get, PacketCoding.CreateGamePacket(4, - msg - )).require) + sendResponse(PacketCoding.CreateGamePacket(0, msg)) case Failure(e) => println("Failed to decode inner packet " + e) } } - case default => failWithError(s"Invalid message received ${default}") + } + + def handleGamePkt(pkt : PlanetSideGamePacket) = { + } def failWithError(error : String) = { @@ -227,36 +67,8 @@ class LoginSessionActor(session : LoginSession) extends Actor with ActorLogging sendResponse(PacketCoding.CreateControlPacket(ConnectionClose())) } - def resetState() : Unit = { - context.become(receive) - - // reset the crypto primitives - cryptoDHState.close - cryptoDHState = new CryptoInterface.CryptoDHState() - - if(cryptoState.isDefined) { - cryptoState.get.close - cryptoState = None - } - - serverChallenge = ByteVector.empty - serverChallengeResult = ByteVector.empty - serverMACBuffer = ByteVector.empty - clientPublicKey = ByteVector.empty - clientChallenge = ByteVector.empty - clientChallengeResult = ByteVector.empty - } - - def sendResponse(cont : PlanetSidePacketContainer) : ByteVector = { - println("SEND: " + cont) - val pkt = PacketCoding.MarshalPacket(cont).require - session.send(pkt) - pkt.toByteVector - } - - def getRandBytes(amount : Int) : ByteVector = { - val array = Array.ofDim[Byte](amount) - random.nextBytes(array) - ByteVector.view(array) + def sendResponse(cont : PlanetSidePacketContainer) = { + log.info("LOGIN SEND: " + cont) + rightRef ! cont } } diff --git a/pslogin/src/main/scala/SessionRouter.scala b/pslogin/src/main/scala/SessionRouter.scala index 863bcaea..d596e357 100644 --- a/pslogin/src/main/scala/SessionRouter.scala +++ b/pslogin/src/main/scala/SessionRouter.scala @@ -1,36 +1,88 @@ // Copyright (c) 2016 PSForever.net to present import java.net.InetSocketAddress -import akka.actor.{Props, ActorRef, ActorLogging, Actor} +import akka.actor._ import scodec.bits._ import scala.collection.mutable final case class RawPacket(data : ByteVector) +final case class ResponsePacket(data : ByteVector) + +case class SessionState(id : Long, address : InetSocketAddress, pipeline : List[ActorRef]) { + def inject(pkt : RawPacket) = pipeline.head ! pkt +} class SessionRouter extends Actor with ActorLogging { - val sessions = mutable.Map[InetSocketAddress, ActorRef]() - var sessionId = 0L + val idBySocket = mutable.Map[InetSocketAddress, Long]() + val sessionById = mutable.Map[Long, SessionState]() + val sessionByActor = mutable.Map[ActorRef, SessionState]() - def receive = { + var sessionId = 0L // this is a connection session, not an actual logged in session ID + var inputRef : ActorRef = ActorRef.noSender + + /* + Login sessions are divided between two actors. the crypto session actor transparently handles all of the cryptographic + setup of the connection. Once a correct crypto session has been established, all packets, after being decrypted + will be passed on to the login session actor. This actor has important state that is used to maintain the login + session. + + > PlanetSide Session Pipeline < + + read() route decrypt + UDP Socket -----> [Session Router] -----> [Crypto Actor] -----> [Session Actor] + ^ | ^ | ^ | + | write() | | encrypt | | response | + +--------------+ +-----------+ +-----------------+ + */ + + def receive = initializing + + def initializing : Receive = { + case Hello() => + inputRef = sender() + context.become(started) + case _ => + log.error("Unknown message") + context.stop(self) + } + + def started : Receive = { case ReceivedPacket(msg, from) => - if(sessions.contains(from)) { - sessions{from} ! RawPacket(msg) + if(idBySocket.contains(from)) { + sessionById{idBySocket{from}}.inject(RawPacket(msg)) } else { log.info("New session from " + from.toString) - val id = newSessionId - val loginSession = new LoginSession(id, sender(), from) - val ref = context.actorOf(Props(new LoginSessionActor(loginSession)), - "login-session" + id.toString) + val session = createNewSession(from) + idBySocket{from} = session.id - sessions{from} = ref + sessionById{session.id} = session + sessionByActor{session.pipeline.head} = session - ref ! RawPacket(msg) + sessionById{session.id}.inject(RawPacket(msg)) } + case ResponsePacket(msg) => + val session = sessionByActor{sender()} + + inputRef ! SendPacket(msg, session.address) case _ => log.error("Unknown message") } + def createNewSession(address : InetSocketAddress) = { + val id = newSessionId + + val cryptoSession = context.actorOf(Props[CryptoSessionActor], + "crypto-session" + id.toString) + val loginSession = context.actorOf(Props[LoginSessionActor], + "login-session" + id.toString) + + // start the pipeline setup + cryptoSession ! HelloFriend(loginSession) + + SessionState(id, address, List(cryptoSession, loginSession)) + } + def newSessionId = { val oldId = sessionId sessionId += 1 diff --git a/pslogin/src/main/scala/UdpListener.scala b/pslogin/src/main/scala/UdpListener.scala index e54a1673..7c85abdd 100644 --- a/pslogin/src/main/scala/UdpListener.scala +++ b/pslogin/src/main/scala/UdpListener.scala @@ -1,13 +1,15 @@ // Copyright (c) 2016 PSForever.net to present import java.net.InetSocketAddress -import akka.actor.{ActorLogging, Actor, ActorRef} +import akka.actor.{Identify, ActorLogging, Actor, ActorRef} import akka.io._ import scodec.bits.ByteVector import scodec.interop.akka._ final case class ReceivedPacket(msg : ByteVector, from : InetSocketAddress) final case class SendPacket(msg : ByteVector, to : InetSocketAddress) +final case class Hello() +final case class HelloFriend(next: ActorRef) class UdpListener(nextActor: ActorRef) extends Actor with ActorLogging { import context.system @@ -19,6 +21,8 @@ class UdpListener(nextActor: ActorRef) extends Actor with ActorLogging { def receive = { case Udp.Bound(local) => println("UDP bound: " + local) + + nextActor ! Hello() context.become(ready(sender())) }