Files
CypherCore/Source/Game/Network/Packets/AuthenticationPackets.cs
T
Fabian a3dc7b3f48 Ported .Net Core commits:
hondacrx:
- Initial commit: Switch to .Net Core 2.0
- Fix build and removed not needed files
Fabi:
- Updated solution platforms.
- Changed folder structure.
- Change library target framework to netstandard2.0.
- Updated solution platforms again...
- Removed windows specific kernel32 function usage (Ctrl-C handler).
2017-10-26 17:23:44 +02:00

444 lines
18 KiB
C#

/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Framework.Cryptography;
using Framework.Dynamic;
using Framework.IO;
using Game.DataStorage;
using System;
using System.Collections.Generic;
using System.Net;
namespace Game.Network.Packets
{
class Ping : ClientPacket
{
public Ping(WorldPacket packet) : base(packet) { }
public override void Read()
{
Serial = _worldPacket.ReadUInt32();
Latency = _worldPacket.ReadUInt32();
}
public uint Serial;
public uint Latency;
}
class Pong : ServerPacket
{
public Pong(uint serial) : base(ServerOpcodes.Pong)
{
Serial = serial;
}
public override void Write()
{
_worldPacket.WriteUInt32(Serial);
}
uint Serial;
}
class AuthChallenge : ServerPacket
{
public AuthChallenge() : base(ServerOpcodes.AuthChallenge) { }
public override void Write()
{
_worldPacket.WriteBytes(DosChallenge);
_worldPacket.WriteBytes(Challenge);
_worldPacket.WriteUInt8(DosZeroBits);
}
public byte[] Challenge = new byte[16];
public byte[] DosChallenge = new byte[32]; // Encryption seeds
public byte DosZeroBits;
}
class AuthSession : ClientPacket
{
public AuthSession(WorldPacket packet) : base(packet) { }
public override void Read()
{
uint realmJoinTicketSize;
DosResponse = _worldPacket.ReadUInt64();
Build = _worldPacket.ReadUInt16();
BuildType = _worldPacket.ReadInt8();
RegionID = _worldPacket.ReadUInt32();
BattlegroupID = _worldPacket.ReadUInt32();
RealmID = _worldPacket.ReadUInt32();
for (var i = 0; i < LocalChallenge.GetLimit(); ++i)
LocalChallenge.Add(_worldPacket.ReadUInt8());
Digest = _worldPacket.ReadBytes(24);
UseIPv6 = _worldPacket.HasBit();
realmJoinTicketSize = _worldPacket.ReadUInt32();
if (realmJoinTicketSize != 0)
RealmJoinTicket = _worldPacket.ReadString(realmJoinTicketSize);
}
public ushort Build;
public sbyte BuildType;
public uint RegionID;
public uint BattlegroupID;
public uint RealmID;
public Array<byte> LocalChallenge = new Array<byte>(16);
public byte[] Digest = new byte[24];
public ulong DosResponse;
public string RealmJoinTicket;
public bool UseIPv6;
}
class AuthResponse : ServerPacket
{
public AuthResponse() : base(ServerOpcodes.AuthResponse)
{
WaitInfo = new Optional<AuthWaitInfo>();
SuccessInfo = new Optional<AuthSuccessInfo>();
}
public override void Write()
{
_worldPacket.WriteUInt32(Result);
_worldPacket.WriteBit(SuccessInfo.HasValue);
_worldPacket.WriteBit(WaitInfo.HasValue);
_worldPacket.FlushBits();
if (SuccessInfo.HasValue)
{
_worldPacket.WriteUInt32(SuccessInfo.Value.VirtualRealmAddress);
_worldPacket.WriteUInt32(SuccessInfo.Value.VirtualRealms.Count);
_worldPacket.WriteUInt32(SuccessInfo.Value.TimeRested);
_worldPacket.WriteUInt8(SuccessInfo.Value.ActiveExpansionLevel);
_worldPacket.WriteUInt8(SuccessInfo.Value.AccountExpansionLevel);
_worldPacket.WriteUInt32(SuccessInfo.Value.TimeSecondsUntilPCKick);
_worldPacket.WriteUInt32(SuccessInfo.Value.AvailableRaces.Count);
_worldPacket.WriteUInt32(SuccessInfo.Value.AvailableClasses.Count);
_worldPacket.WriteUInt32(SuccessInfo.Value.Templates.Count);
_worldPacket.WriteUInt32(SuccessInfo.Value.CurrencyID);
_worldPacket.WriteUInt32(SuccessInfo.Value.Time);
foreach (var race in SuccessInfo.Value.AvailableRaces)
{
_worldPacket.WriteUInt8(race.Key); /// the current race
_worldPacket.WriteUInt8(race.Value); /// the required Expansion
}
foreach (var klass in SuccessInfo.Value.AvailableClasses)
{
_worldPacket.WriteUInt8(klass.Key); /// the current class
_worldPacket.WriteUInt8(klass.Value); /// the required Expansion
}
_worldPacket.WriteBit(SuccessInfo.Value.IsExpansionTrial);
_worldPacket.WriteBit(SuccessInfo.Value.ForceCharacterTemplate);
_worldPacket.WriteBit(SuccessInfo.Value.NumPlayersHorde.HasValue);
_worldPacket.WriteBit(SuccessInfo.Value.NumPlayersAlliance.HasValue);
_worldPacket.FlushBits();
{
_worldPacket.WriteUInt32(SuccessInfo.Value.Billing.BillingPlan);
_worldPacket.WriteUInt32(SuccessInfo.Value.Billing.TimeRemain);
// 3x same bit is not a mistake - preserves legacy client behavior of BillingPlanFlags::SESSION_IGR
_worldPacket.WriteBit(SuccessInfo.Value.Billing.InGameRoom); // inGameRoom check in function checking which lua event to fire when remaining time is near end - BILLING_NAG_DIALOG vs IGR_BILLING_NAG_DIALOG
_worldPacket.WriteBit(SuccessInfo.Value.Billing.InGameRoom); // inGameRoom lua return from Script_GetBillingPlan
_worldPacket.WriteBit(SuccessInfo.Value.Billing.InGameRoom); // not used anywhere in the client
_worldPacket.FlushBits();
}
if (SuccessInfo.Value.NumPlayersHorde.HasValue)
_worldPacket.WriteUInt16(SuccessInfo.Value.NumPlayersHorde.Value);
if (SuccessInfo.Value.NumPlayersAlliance.HasValue)
_worldPacket.WriteUInt16(SuccessInfo.Value.NumPlayersAlliance.Value);
foreach (VirtualRealmInfo virtualRealm in SuccessInfo.Value.VirtualRealms)
virtualRealm.Write(_worldPacket);
foreach (var templat in SuccessInfo.Value.Templates)
{
_worldPacket.WriteUInt32(templat.TemplateSetId);
_worldPacket.WriteUInt32(templat.Classes.Count);
foreach (var templateClass in templat.Classes)
{
_worldPacket.WriteUInt8(templateClass.ClassID);
_worldPacket.WriteUInt8(templateClass.FactionGroup);
}
_worldPacket.WriteBits(templat.Name.Length, 7);
_worldPacket.WriteBits(templat.Description.Length, 10);
_worldPacket.FlushBits();
_worldPacket.WriteString(templat.Name);
_worldPacket.WriteString(templat.Description);
}
}
if (WaitInfo.HasValue)
WaitInfo.Value.Write(_worldPacket);
}
public Optional<AuthSuccessInfo> SuccessInfo; // contains the packet data in case that it has account information (It is never set when WaitInfo is set), otherwise its contents are undefined.
public Optional<AuthWaitInfo> WaitInfo; // contains the queue wait information in case the account is in the login queue.
public BattlenetRpcErrorCode Result; // the result of the authentication process, possible values are @ref BattlenetRpcErrorCode
public class AuthSuccessInfo
{
public byte AccountExpansionLevel; // the current expansion of this account, the possible values are in @ref Expansions
public byte ActiveExpansionLevel; // the current server expansion, the possible values are in @ref Expansions
public uint TimeRested; // affects the return value of the GetBillingTimeRested() client API call, it is the number of seconds you have left until the experience points and loot you receive from creatures and quests is reduced. It is only used in the Asia region in retail, it's not implemented in TC and will probably never be.
public uint VirtualRealmAddress; // a special identifier made from the Index, BattleGroup and Region. @todo implement
public uint TimeSecondsUntilPCKick; // @todo research
public uint CurrencyID; // this is probably used for the ingame shop. @todo implement
public uint Time;
public BillingInfo Billing;
public List<VirtualRealmInfo> VirtualRealms = new List<VirtualRealmInfo>(); // list of realms connected to this one (inclusive) @todo implement
public List<CharacterTemplate> Templates = new List<CharacterTemplate>(); // list of pre-made character templates. @todo implement
public Dictionary<byte, byte> AvailableClasses; // the minimum AccountExpansion required to select the classes
public Dictionary<byte, byte> AvailableRaces; // the minimum AccountExpansion required to select the races
public bool IsExpansionTrial;
public bool ForceCharacterTemplate; // forces the client to always use a character template when creating a new character. @see Templates. @todo implement
public Optional<ushort> NumPlayersHorde; // number of horde players in this realm. @todo implement
public Optional<ushort> NumPlayersAlliance; // number of alliance players in this realm. @todo implement
public struct BillingInfo
{
public uint BillingPlan;
public uint TimeRemain;
public bool InGameRoom;
}
}
}
class WaitQueueUpdate : ServerPacket
{
public WaitQueueUpdate() : base(ServerOpcodes.WaitQueueUpdate) { }
public override void Write()
{
WaitInfo.Write(_worldPacket);
}
public AuthWaitInfo WaitInfo;
}
class WaitQueueFinish : ServerPacket
{
public WaitQueueFinish() : base(ServerOpcodes.WaitQueueFinish) { }
public override void Write() { }
}
class ConnectTo : ServerPacket
{
public ConnectTo() : base(ServerOpcodes.ConnectTo)
{
Payload = new ConnectPayload();
Payload.PanamaKey = "F41DCB2D728CF3337A4FF338FA89DB01BBBE9C3B65E9DA96268687353E48B94C".ToByteArray();
Payload.Adler32 = 0xA0A66C10;
Crypt = new RsaCrypt();
Crypt.InitializeEncryption(RsaStore.P, RsaStore.Q, RsaStore.DP, RsaStore.DQ, RsaStore.InverseQ);
}
public override void Write()
{
byte[] port = BitConverter.GetBytes((ushort)Payload.Where.Port);
byte[] address = new byte[16];
byte addressType = 3;
if (Payload.Where.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
Buffer.BlockCopy(Payload.Where.Address.GetAddressBytes(), 0, address, 0, 4);
addressType = 1;
}
else
{
Buffer.BlockCopy(Payload.Where.Address.GetAddressBytes(), 0, address, 0, 16);
addressType = 2;
}
HmacHash hmacHash = new HmacHash(RsaStore.WherePacketHmac);
hmacHash.Process(address, 16);
hmacHash.Process(BitConverter.GetBytes(addressType), 1);
hmacHash.Process(port, 2);
hmacHash.Process(Haiku);
hmacHash.Process(Payload.PanamaKey, 32);
hmacHash.Process(PiDigits, 108);
hmacHash.Finish(BitConverter.GetBytes(Payload.XorMagic), 1);
ByteBuffer payload = new ByteBuffer();
payload.WriteUInt32(Payload.Adler32);
payload.WriteUInt8(addressType);
payload.WriteBytes(address, 16);
payload.WriteUInt16(Payload.Where.Port);
payload.WriteString(Haiku);
payload.WriteBytes(Payload.PanamaKey, 32);
payload.WriteBytes(PiDigits, 108);
payload.WriteUInt8(Payload.XorMagic);
payload.WriteBytes(hmacHash.Digest);
_worldPacket.WriteUInt64(Key);
_worldPacket.WriteUInt32(Serial);
_worldPacket.WriteBytes(Crypt.Encrypt(payload.GetData()), 256);
_worldPacket.WriteUInt8(Con);
}
public ulong Key;
public ConnectToSerial Serial;
public ConnectPayload Payload;
public byte Con;
public string Haiku = "An island of peace\nCorruption is brought ashore\nPandarens will rise\n\0\0\0";
public byte[] PiDigits = { 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95, 0x02, 0x88, 0x41, 0x97,
0x16, 0x93, 0x99, 0x37, 0x51, 0x05, 0x82, 0x09, 0x74, 0x94, 0x45, 0x92, 0x30, 0x78, 0x16, 0x40, 0x62, 0x86, 0x20, 0x89,
0x98, 0x62, 0x80, 0x34, 0x82, 0x53, 0x42, 0x11, 0x70, 0x67, 0x98, 0x21, 0x48, 0x08, 0x65, 0x13, 0x28, 0x23, 0x06, 0x64,
0x70, 0x93, 0x84, 0x46, 0x09, 0x55, 0x05, 0x82, 0x23, 0x17, 0x25, 0x35, 0x94, 0x08, 0x12, 0x84, 0x81, 0x11, 0x74, 0x50,
0x28, 0x41, 0x02, 0x70, 0x19, 0x38, 0x52, 0x11, 0x05, 0x55, 0x96, 0x44, 0x62, 0x29, 0x48, 0x95, 0x49, 0x30, 0x38, 0x19,
0x64, 0x42, 0x88, 0x10, 0x97, 0x56, 0x65, 0x93, 0x34, 0x46, 0x12, 0x84, 0x75, 0x64, 0x82, 0x33, 0x78, 0x67, 0x83, 0x16,
0x52, 0x71, 0x20, 0x19, 0x09, 0x14, 0x56, 0x48, 0x56, 0x69 };
public class ConnectPayload
{
public IPEndPoint Where;
public uint Adler32;
public byte XorMagic = 0x2A;
public byte[] PanamaKey = new byte[32];
}
RsaCrypt Crypt;
}
class AuthContinuedSession : ClientPacket
{
public AuthContinuedSession(WorldPacket packet) : base(packet) { }
public override void Read()
{
DosResponse = _worldPacket.ReadUInt64();
Key = _worldPacket.ReadUInt64();
LocalChallenge = _worldPacket.ReadBytes(16);
Digest = _worldPacket.ReadBytes(24);
}
public ulong DosResponse;
public ulong Key;
public byte[] LocalChallenge = new byte[16];
public byte[] Digest = new byte[24];
}
class ResumeComms : ServerPacket
{
public ResumeComms(ConnectionType connection) : base(ServerOpcodes.ResumeComms, connection) { }
public override void Write() { }
}
class ConnectToFailed : ClientPacket
{
public ConnectToFailed(WorldPacket packet) : base(packet) { }
public override void Read()
{
Serial = (ConnectToSerial)_worldPacket.ReadUInt32();
Con = _worldPacket.ReadUInt8();
}
public ConnectToSerial Serial;
byte Con;
}
class EnableEncryption : ServerPacket
{
public EnableEncryption() : base(ServerOpcodes.EnableEncryption) { }
public override void Write() { }
}
//Structs
public struct AuthWaitInfo
{
public void Write(WorldPacket data)
{
data.WriteUInt32(WaitCount);
data.WriteUInt32(WaitTime);
data.WriteBit(HasFCM);
data.FlushBits();
}
public uint WaitCount; // position of the account in the login queue
public uint WaitTime; // Wait time in login queue in minutes, if sent queued and this value is 0 client displays "unknown time"
public bool HasFCM; // true if the account has a forced character migration pending. @todo implement
}
struct VirtualRealmNameInfo
{
public VirtualRealmNameInfo(bool isHomeRealm, bool isInternalRealm, string realmNameActual, string realmNameNormalized)
{
IsLocal = isHomeRealm;
IsInternalRealm = isInternalRealm;
RealmNameActual = realmNameActual;
RealmNameNormalized = realmNameNormalized;
}
public void Write(WorldPacket data)
{
data.WriteBit(IsLocal);
data.WriteBit(IsInternalRealm);
data.WriteBits(RealmNameActual.Length, 8);
data.WriteBits(RealmNameNormalized.Length, 8);
data.FlushBits();
data.WriteString(RealmNameActual);
data.WriteString(RealmNameNormalized);
}
public bool IsLocal; // true if the realm is the same as the account's home realm
public bool IsInternalRealm; // @todo research
public string RealmNameActual; // the name of the realm
public string RealmNameNormalized; // the name of the realm without spaces
}
struct VirtualRealmInfo
{
public VirtualRealmInfo(uint realmAddress, bool isHomeRealm, bool isInternalRealm, string realmNameActual, string realmNameNormalized)
{
RealmAddress = realmAddress;
RealmNameInfo = new VirtualRealmNameInfo(isHomeRealm, isInternalRealm, realmNameActual, realmNameNormalized);
}
public void Write(WorldPacket data)
{
data.WriteUInt32(RealmAddress);
RealmNameInfo.Write(data);
}
public uint RealmAddress; // the virtual address of this realm, constructed as RealmHandle::Region << 24 | RealmHandle::Battlegroup << 16 | RealmHandle::Index
public VirtualRealmNameInfo RealmNameInfo;
}
}