mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-19 18:14:44 +00:00
New session pipeline and able to see server selection
This commit is contained in:
parent
0d986bcb29
commit
53488613d6
302
pslogin/src/main/scala/CryptoSessionActor.scala
Normal file
302
pslogin/src/main/scala/CryptoSessionActor.scala
Normal file
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue