From ddf1d2d1a34d4040821f49608ae5fdfa764d6147 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Sun, 13 Sep 2015 18:21:28 -0400 Subject: [PATCH] Added account and select character packet creators. Fixed wrong field being read for session id. Most of the server is now NOT hardcoded and customizable from the DB. Only hardcoded packet left is the initial handshake. --- FFXIVClassic_Lobby_Server/ClientConnection.cs | 1 + FFXIVClassic_Lobby_Server/Database.cs | 15 ++- .../FFXIVClassic_Lobby_Server.csproj | 3 + FFXIVClassic_Lobby_Server/PacketProcessor.cs | 78 ++++++++------- FFXIVClassic_Lobby_Server/common/Blowfish.cs | 6 +- .../dataobjects/Account.cs | 14 +++ .../dataobjects/CharaInfo.cs | 10 +- .../packets/AccountListPacket.cs | 99 +++++++++++++++++++ .../packets/PacketStructs.cs | 51 ++++------ .../packets/SelectCharacterConfirmPacket.cs | 59 +++++++++++ 10 files changed, 259 insertions(+), 77 deletions(-) create mode 100644 FFXIVClassic_Lobby_Server/dataobjects/Account.cs create mode 100644 FFXIVClassic_Lobby_Server/packets/AccountListPacket.cs create mode 100644 FFXIVClassic_Lobby_Server/packets/SelectCharacterConfirmPacket.cs diff --git a/FFXIVClassic_Lobby_Server/ClientConnection.cs b/FFXIVClassic_Lobby_Server/ClientConnection.cs index e326b3e1..771dd3df 100644 --- a/FFXIVClassic_Lobby_Server/ClientConnection.cs +++ b/FFXIVClassic_Lobby_Server/ClientConnection.cs @@ -26,6 +26,7 @@ namespace FFXIVClassic_Lobby_Server //Instance Stuff public uint currentUserId = 0; public uint currentAccount; + public string currentSessionToken; //Chara Creation public string newCharaName; diff --git a/FFXIVClassic_Lobby_Server/Database.cs b/FFXIVClassic_Lobby_Server/Database.cs index b3d7fe43..7e6f76de 100644 --- a/FFXIVClassic_Lobby_Server/Database.cs +++ b/FFXIVClassic_Lobby_Server/Database.cs @@ -7,10 +7,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using FFXIVClassic_Lobby_Server.common; namespace FFXIVClassic_Lobby_Server { - //charState: 0 - Reserved, 1 - Deleted, 2 - Inactive, 3 - Active + //charState: 0 - Reserved, 1 - Inactive, 2 - Active class Database { @@ -37,7 +38,7 @@ namespace FFXIVClassic_Lobby_Server finally { conn.Dispose(); - } + } } return id; } @@ -93,6 +94,8 @@ namespace FFXIVClassic_Lobby_Server { conn.Dispose(); } + + Log.database(String.Format("CID={0} created on 'characters' table.", cid)); } return alreadyExists; @@ -107,7 +110,7 @@ namespace FFXIVClassic_Lobby_Server conn.Open(); MySqlCommand cmd = new MySqlCommand(); cmd.Connection = conn; - cmd.CommandText = "UPDATE characters SET state=3, charaInfo=@encodedInfo WHERE userId=@userId AND id=@cid"; + cmd.CommandText = "UPDATE characters SET state=2, charaInfo=@encodedInfo WHERE userId=@userId AND id=@cid"; cmd.Prepare(); cmd.Parameters.AddWithValue("@userId", accountId); @@ -125,6 +128,8 @@ namespace FFXIVClassic_Lobby_Server { conn.Dispose(); } + + Log.database(String.Format("CID={0} state updated to active(2).", cid)); } } @@ -167,6 +172,8 @@ namespace FFXIVClassic_Lobby_Server conn.Dispose(); } + Log.database(String.Format("CID={0} name updated to \"{1}\".", characterId, newName)); + return false; } } @@ -196,6 +203,8 @@ namespace FFXIVClassic_Lobby_Server conn.Dispose(); } } + + Log.database(String.Format("CID={0} deleted.", characterId)); } public static List getServers() diff --git a/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj b/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj index e30d33c2..524b1b38 100644 --- a/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj +++ b/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj @@ -58,6 +58,7 @@ + @@ -74,9 +75,11 @@ + + diff --git a/FFXIVClassic_Lobby_Server/PacketProcessor.cs b/FFXIVClassic_Lobby_Server/PacketProcessor.cs index 6ed7b0ed..33066905 100644 --- a/FFXIVClassic_Lobby_Server/PacketProcessor.cs +++ b/FFXIVClassic_Lobby_Server/PacketProcessor.cs @@ -84,8 +84,8 @@ namespace FFXIVClassic_Lobby_Server List subPackets = packet.getSubpackets(); foreach (SubPacket subpacket in subPackets) - { - + { + subpacket.debugPrintSubPacket(); switch (subpacket.header.opcode) { case 0x03: @@ -127,27 +127,39 @@ namespace FFXIVClassic_Lobby_Server private void ProcessSessionAcknowledgement(ClientConnection client, SubPacket packet) { - PacketStructs.SessionPacket sessionPacket = PacketStructs.toSessionStruct(packet.data); - String sessionId = sessionPacket.session; + PacketStructs.SessionPacket sessionPacket = PacketStructs.toSessionStruct(packet.data); String clientVersion = sessionPacket.version; - Log.info(String.Format("Got acknowledgment for secure session.")); - Log.info(String.Format("SESSION ID: {0}", sessionId)); + Log.info(String.Format("Got acknowledgment for secure session.")); Log.info(String.Format("CLIENT VERSION: {0}", clientVersion)); - uint userId = Database.getUserIdFromSession(sessionId); + uint userId = Database.getUserIdFromSession(sessionPacket.session); client.currentUserId = userId; + client.currentSessionToken = sessionPacket.session; ; if (userId == 0) { - //client.disconnect(); - Log.info(String.Format("Invalid session, kicking...")); + ErrorPacket errorPacket = new ErrorPacket(sessionPacket.sequence, 0, 0, 13001, "Your session has expired, please login again."); + SubPacket subpacket = errorPacket.buildPacket(); + BasePacket errorBasePacket = BasePacket.createPacket(subpacket, true, false); + BasePacket.encryptPacket(client.blowfish, errorBasePacket); + client.queuePacket(errorBasePacket); + + Log.info(String.Format("Invalid session, kicking...")); + return; } Log.info(String.Format("USER ID: {0}", userId)); - BasePacket outgoingPacket = new BasePacket("./packets/loginAck.bin"); - BasePacket.encryptPacket(client.blowfish, outgoingPacket); - client.queuePacket(outgoingPacket); + + List accountList = new List(); + Account defaultAccount = new Account(); + defaultAccount.id = 1; + defaultAccount.name = "FINAL FANTASY XIV"; + accountList.Add(defaultAccount); + AccountListPacket listPacket = new AccountListPacket(1, accountList); + BasePacket basePacket = BasePacket.createPacket(listPacket.buildPackets(), true, false); + BasePacket.encryptPacket(client.blowfish, basePacket); + client.queuePacket(basePacket); } private void ProcessGetCharacters(ClientConnection client, SubPacket packet) @@ -163,33 +175,29 @@ namespace FFXIVClassic_Lobby_Server private void ProcessSelectCharacter(ClientConnection client, SubPacket packet) { - uint characterId = 0; - using (BinaryReader binReader = new BinaryReader(new MemoryStream(packet.data))) + FFXIVClassic_Lobby_Server.packets.PacketStructs.SelectCharRequestPacket selectCharRequest = PacketStructs.toSelectCharRequestStruct(packet.data); + + Log.info(String.Format("{0} => Select character id {1}", client.currentUserId == 0 ? client.getAddress() : "User " + client.currentUserId, selectCharRequest.characterId)); + + Character chara = Database.getCharacter(client.currentUserId, selectCharRequest.characterId); + World world = null; + + if (chara != null) + world = Database.getServer(chara.serverId); + + if (world == null) { - binReader.BaseStream.Seek(0x8, SeekOrigin.Begin); - characterId = binReader.ReadUInt32(); - binReader.Close(); + ErrorPacket errorPacket = new ErrorPacket(selectCharRequest.sequence, 0, 0, 13001, "World does not exist or is inactive."); + SubPacket subpacket = errorPacket.buildPacket(); + BasePacket basePacket = BasePacket.createPacket(subpacket, true, false); + BasePacket.encryptPacket(client.blowfish, basePacket); + client.queuePacket(basePacket); + return; } - Log.info(String.Format("{0} => Select character id {1}", client.currentUserId == 0 ? client.getAddress() : "User " + client.currentUserId, characterId)); - - String serverIp = "141.117.161.40"; - ushort port = 54992; - BitConverter.GetBytes(port); - BasePacket outgoingPacket = new BasePacket("./packets/selectChar.bin"); - - //Write Character ID and Server info - using (BinaryWriter binWriter = new BinaryWriter(new MemoryStream(outgoingPacket.data))) - { - binWriter.Seek(0x28, SeekOrigin.Begin); - binWriter.Write(characterId); - binWriter.Seek(0x78, SeekOrigin.Begin); - binWriter.Write(System.Text.Encoding.ASCII.GetBytes(serverIp)); - binWriter.Seek(0x76, SeekOrigin.Begin); - binWriter.Write(port); - binWriter.Close(); - } + SelectCharacterConfirmPacket connectCharacter = new SelectCharacterConfirmPacket(selectCharRequest.sequence, selectCharRequest.characterId, client.currentSessionToken, world.address, world.port, selectCharRequest.ticket); + BasePacket outgoingPacket = BasePacket.createPacket(connectCharacter.buildPackets(), true, false); BasePacket.encryptPacket(client.blowfish, outgoingPacket); client.queuePacket(outgoingPacket); } diff --git a/FFXIVClassic_Lobby_Server/common/Blowfish.cs b/FFXIVClassic_Lobby_Server/common/Blowfish.cs index 366ba5e1..25eab806 100644 --- a/FFXIVClassic_Lobby_Server/common/Blowfish.cs +++ b/FFXIVClassic_Lobby_Server/common/Blowfish.cs @@ -8,11 +8,11 @@ namespace FFXIVClassic_Lobby_Server.common public class Blowfish { - [DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] + [DllImport("./Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] private static extern short initializeBlowfish(byte[] key, short keySize, uint[] P, uint[,] S); - [DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] + [DllImport("./Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] private static extern void blowfish_encipher(ref int xl, ref int xr, uint[] P); - [DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] + [DllImport("./Blowfish.dll", CallingConvention = CallingConvention.Cdecl)] private static extern void blowfish_decipher(ref int xl, ref int xr, uint[] P); private uint[] P = new uint[16+2]; diff --git a/FFXIVClassic_Lobby_Server/dataobjects/Account.cs b/FFXIVClassic_Lobby_Server/dataobjects/Account.cs new file mode 100644 index 00000000..437b85ab --- /dev/null +++ b/FFXIVClassic_Lobby_Server/dataobjects/Account.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Lobby_Server.dataobjects +{ + class Account + { + public UInt32 id; + public string name; + } +} diff --git a/FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs b/FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs index 5609d829..765794c3 100644 --- a/FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs +++ b/FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs @@ -141,6 +141,14 @@ namespace FFXIVClassic_Lobby_Server.dataobjects { byte[] data; + mainHand = 79707136; + offHand = 32509954; + headGear = 43008; + bodyGear = 43008; + legsGear = 43008; + handsGear = 43008; + feetGear = 43008; + using (MemoryStream stream = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(stream)) @@ -240,8 +248,6 @@ namespace FFXIVClassic_Lobby_Server.dataobjects } data = stream.GetBuffer(); - - File.WriteAllBytes("./packets/out.bin",data); } return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_'); diff --git a/FFXIVClassic_Lobby_Server/packets/AccountListPacket.cs b/FFXIVClassic_Lobby_Server/packets/AccountListPacket.cs new file mode 100644 index 00000000..743ace70 --- /dev/null +++ b/FFXIVClassic_Lobby_Server/packets/AccountListPacket.cs @@ -0,0 +1,99 @@ +using FFXIVClassic_Lobby_Server.dataobjects; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Lobby_Server.packets +{ + class AccountListPacket + { + public const ushort OPCODE = 0x0C; + public const ushort MAXPERPACKET = 8; + + private UInt64 sequence; + private List accountList; + + public AccountListPacket(UInt64 sequence, List accountList) + { + this.sequence = sequence; + this.accountList = accountList; + } + + public List buildPackets() + { + List subPackets = new List(); + + int accountCount = 0; + int totalCount = 0; + + MemoryStream memStream = null; + BinaryWriter binWriter = null; + + foreach (Account account in accountList) + { + if (totalCount == 0 || accountCount % MAXPERPACKET == 0) + { + memStream = new MemoryStream(0x280); + binWriter = new BinaryWriter(memStream); + + //Write List Info + binWriter.Write((UInt64)sequence); + byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count); + binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker)); + binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (UInt32)(accountList.Count - totalCount) : (UInt32)MAXPERPACKET); + binWriter.Write((byte)0); + binWriter.Write((UInt16)0); + } + + //Write Entries + binWriter.Write((UInt32)account.id); + binWriter.Write((UInt32)0); + binWriter.Write(Encoding.ASCII.GetBytes(account.name.PadRight(0x40, '\0'))); + + accountCount++; + totalCount++; + + //Send this chunk of world list + if (accountCount >= MAXPERPACKET) + { + byte[] data = memStream.GetBuffer(); + binWriter.Dispose(); + memStream.Dispose(); + SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data); + subPackets.Add(subpacket); + accountCount = 0; + } + + } + + //If there is anything left that was missed or the list is empty + if (accountCount > 0 || accountList.Count == 0) + { + if (accountList.Count == 0) + { + memStream = new MemoryStream(0x210); + binWriter = new BinaryWriter(memStream); + + //Write Empty List Info + binWriter.Write((UInt64)sequence); + byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count); + binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker)); + binWriter.Write((UInt32)0); + binWriter.Write((byte)0); + binWriter.Write((UInt16)0); + } + + byte[] data = memStream.GetBuffer(); + binWriter.Dispose(); + memStream.Dispose(); + SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data); + subPackets.Add(subpacket); + } + + return subPackets; + } + } +} diff --git a/FFXIVClassic_Lobby_Server/packets/PacketStructs.cs b/FFXIVClassic_Lobby_Server/packets/PacketStructs.cs index 76e31f4a..683954ca 100644 --- a/FFXIVClassic_Lobby_Server/packets/PacketStructs.cs +++ b/FFXIVClassic_Lobby_Server/packets/PacketStructs.cs @@ -13,13 +13,13 @@ namespace FFXIVClassic_Lobby_Server.packets public unsafe struct SessionPacket { [FieldOffset(0)] - public UInt64 sequence; + public UInt64 sequence; + [FieldOffset(0x10)] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x40)] + public String session; [FieldOffset(0x50)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] - public String version; - [FieldOffset(0x70)] - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] - public String session; + public String version; } [StructLayout(LayoutKind.Sequential)] @@ -37,38 +37,13 @@ namespace FFXIVClassic_Lobby_Server.packets public String characterInfoEncoded; } - //Response Packets [StructLayout(LayoutKind.Sequential)] - public unsafe struct ReserveCharaResponse + public unsafe struct SelectCharRequestPacket { public UInt64 sequence; - public uint errorCode; - public uint statusCode; - public uint errorId; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x2BB)] - public String errorMessage; - } - - [StructLayout(LayoutKind.Sequential)] - public unsafe struct MakeCharaResponse - { - public UInt64 sequence; - public uint errorCode; - public uint statusCode; - public uint errorId; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x2BB)] - public String errorMessage; - } - - [StructLayout(LayoutKind.Sequential)] - public unsafe struct DeleteCharaResponse - { - public UInt64 sequence; - public uint errorCode; - public uint statusCode; - public uint errorId; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x2BB)] - public String errorMessage; + public uint characterId; + public uint unknownId; + public UInt64 ticket; } public static unsafe CharacterRequestPacket toCharacterRequestStruct(byte[] bytes) @@ -87,6 +62,14 @@ namespace FFXIVClassic_Lobby_Server.packets } } + public static unsafe SelectCharRequestPacket toSelectCharRequestStruct(byte[] bytes) + { + fixed (byte* pdata = &bytes[0]) + { + return (SelectCharRequestPacket)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SelectCharRequestPacket)); + } + } + public static byte[] StructureToByteArray(object obj) { int len = Marshal.SizeOf(obj); diff --git a/FFXIVClassic_Lobby_Server/packets/SelectCharacterConfirmPacket.cs b/FFXIVClassic_Lobby_Server/packets/SelectCharacterConfirmPacket.cs new file mode 100644 index 00000000..5d00437f --- /dev/null +++ b/FFXIVClassic_Lobby_Server/packets/SelectCharacterConfirmPacket.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Lobby_Server.packets +{ + class SelectCharacterConfirmPacket + { + public const ushort OPCODE = 0x0F; + + private UInt64 sequence; + private UInt32 characterId; + private string sessionToken; + private string worldIp; + private UInt16 worldPort; + private UInt64 selectCharTicket; + + public SelectCharacterConfirmPacket(UInt64 sequence, UInt32 characterId, string sessionToken, string worldIp, ushort worldPort, UInt64 selectCharTicket) + { + this.sequence = sequence; + this.characterId = characterId; + this.sessionToken = sessionToken; + this.worldIp = worldIp; + this.worldPort = worldPort; + this.selectCharTicket = selectCharTicket; + } + + public List buildPackets() + { + List subPackets = new List(); + + byte[] data; + + using (MemoryStream memStream = new MemoryStream(0x98)) + { + using (BinaryWriter binWriter = new BinaryWriter(memStream)) + { + binWriter.Write((UInt64)sequence); + binWriter.Write((UInt32)characterId); //ActorId + binWriter.Write((UInt32)characterId); //CharacterId + binWriter.Write((UInt32)0); + binWriter.Write(Encoding.ASCII.GetBytes(sessionToken.PadRight(0x42, '\0'))); //Session Token + binWriter.Write((UInt16)worldPort); //World Port + binWriter.Write(Encoding.ASCII.GetBytes(worldIp.PadRight(0x38, '\0'))); //World Hostname/IP + binWriter.Write((UInt64)selectCharTicket); //Ticket or Handshake of somekind + } + data = memStream.GetBuffer(); + } + + SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data); + subPackets.Add(subpacket); + + return subPackets; + } + } +}