Files
CypherCore/Source/BNetServer/Networking/Session.cs
T
2020-07-12 00:06:43 -04:00

280 lines
9.4 KiB
C#

// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Bgs.Protocol;
using Framework.Constants;
using Framework.Database;
using Framework.IO;
using Framework.Networking;
using Framework.Realm;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
namespace BNetServer.Networking
{
partial class Session : SSLSocket
{
AccountInfo accountInfo;
GameAccountInfo gameAccountInfo;
string locale;
string os;
uint build;
string ipCountry;
byte[] clientSecret;
bool authed;
uint requestToken;
AsyncCallbackProcessor<QueryCallback> queryProcessor;
Dictionary<uint, Action<CodedInputStream>> responseCallbacks;
public Session(Socket socket) : base(socket)
{
clientSecret = new byte[32];
queryProcessor = new AsyncCallbackProcessor<QueryCallback>();
responseCallbacks = new Dictionary<uint, Action<CodedInputStream>>();
}
public override void Accept()
{
string ipAddress = GetRemoteIpEndPoint().ToString();
Log.outInfo(LogFilter.Network, $"{GetClientInfo()} Connection Accepted.");
// Verify that this IP is not in the ip_banned table
DB.Login.Execute(DB.Login.GetPreparedStatement(LoginStatements.DelExpiredIpBans));
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SelIpInfo);
stmt.AddValue(0, ipAddress);
stmt.AddValue(1, BitConverter.ToUInt32(GetRemoteIpEndPoint().Address.GetAddressBytes(), 0));
queryProcessor.AddCallback(DB.Login.AsyncQuery(stmt).WithCallback(async result =>
{
if (!result.IsEmpty())
{
bool banned = false;
do
{
if (result.Read<ulong>(0) != 0)
banned = true;
if (!string.IsNullOrEmpty(result.Read<string>(1)))
ipCountry = result.Read<string>(1);
} while (result.NextRow());
if (banned)
{
Log.outDebug(LogFilter.Session, $"{GetClientInfo()} trying to login with banned ipaddress!");
CloseSocket();
return;
}
}
await AsyncHandshake(Global.LoginServiceMgr.GetCertificate());
}));
}
public override bool Update()
{
if (!base.Update())
return false;
queryProcessor.ProcessReadyCallbacks();
return true;
}
public async override void ReadHandler(byte[] data, int receivedLength)
{
if (!IsOpen())
return;
var stream = new CodedInputStream(data);
while (!stream.IsAtEnd)
{
var header = new Header();
stream.ReadMessage(header);
if (header.ServiceId != 0xFE && header.ServiceHash != 0)
{
var handler = Global.LoginServiceMgr.GetHandler(header.ServiceHash, header.MethodId);
if (handler != null)
handler.Invoke(this, header.Token, stream);
else
{
Log.outError(LogFilter.ServiceProtobuf, $"{GetClientInfo()} tried to call not implemented methodId: {header.MethodId} for servicehash: {header.ServiceHash}");
SendResponse(header.Token, BattlenetRpcErrorCode.RpcNotImplemented);
}
}
else
{
var handler = responseCallbacks.LookupByKey(header.Token);
if (handler != null)
{
handler(stream);
responseCallbacks.Remove(header.Token);
}
}
}
await AsyncRead();
}
public async void SendResponse(uint token, IMessage response)
{
Header header = new Header();
header.Token = token;
header.ServiceId = 0xFE;
header.Size = (uint)response.CalculateSize();
ByteBuffer buffer = new ByteBuffer();
buffer.WriteBytes(GetHeaderSize(header), 2);
buffer.WriteBytes(header.ToByteArray());
buffer.WriteBytes(response.ToByteArray());
await AsyncWrite(buffer.GetData());
}
public async void SendResponse(uint token, BattlenetRpcErrorCode status)
{
Header header = new Header();
header.Token = token;
header.Status = (uint)status;
header.ServiceId = 0xFE;
ByteBuffer buffer = new ByteBuffer();
buffer.WriteBytes(GetHeaderSize(header), 2);
buffer.WriteBytes(header.ToByteArray());
await AsyncWrite(buffer.GetData());
}
public async void SendRequest(uint serviceHash, uint methodId, IMessage request)
{
Header header = new Header();
header.ServiceId = 0;
header.ServiceHash = serviceHash;
header.MethodId = methodId;
header.Size = (uint)request.CalculateSize();
header.Token = requestToken++;
ByteBuffer buffer = new ByteBuffer();
buffer.WriteBytes(GetHeaderSize(header), 2);
buffer.WriteBytes(header.ToByteArray());
buffer.WriteBytes(request.ToByteArray());
await AsyncWrite(buffer.GetData());
}
public byte[] GetHeaderSize(Header header)
{
var size = (ushort)header.CalculateSize();
byte[] bytes = new byte[2];
bytes[0] = (byte)((size >> 8) & 0xff);
bytes[1] = (byte)(size & 0xff);
var headerSizeBytes = BitConverter.GetBytes((ushort)header.CalculateSize());
Array.Reverse(headerSizeBytes);
return bytes;
}
public string GetClientInfo()
{
string stream = '[' + GetRemoteIpEndPoint().ToString();
if (accountInfo != null && !accountInfo.Login.IsEmpty())
stream += ", Account: " + accountInfo.Login;
if (gameAccountInfo != null)
stream += ", Game account: " + gameAccountInfo.Name;
stream += ']';
return stream;
}
}
public class AccountInfo
{
public uint Id;
public string Login;
public bool IsLockedToIP;
public string LockCountry;
public string LastIP;
public uint LoginTicketExpiry;
public bool IsBanned;
public bool IsPermanenetlyBanned;
public string PasswordVerifier;
public string Salt;
public Dictionary<uint, GameAccountInfo> GameAccounts;
public AccountInfo(SQLResult result)
{
Id = result.Read<uint>(0);
Login = result.Read<string>(1);
IsLockedToIP = result.Read<bool>(2);
LockCountry = result.Read<string>(3);
LastIP = result.Read<string>(4);
LoginTicketExpiry = result.Read<uint>(5);
IsBanned = result.Read<ulong>(6) != 0;
IsPermanenetlyBanned = result.Read<ulong>(7) != 0;
PasswordVerifier = result.Read<string>(9);
Salt = result.Read<string>(10);
GameAccounts = new Dictionary<uint, GameAccountInfo>();
const int GameAccountFieldsOffset = 8;
do
{
var account = new GameAccountInfo(result.GetFields(), GameAccountFieldsOffset);
GameAccounts[result.Read<uint>(GameAccountFieldsOffset)] = account;
} while (result.NextRow());
}
}
public class GameAccountInfo
{
public uint Id;
public string Name;
public string DisplayName;
public uint UnbanDate;
public bool IsBanned;
public bool IsPermanenetlyBanned;
public AccountTypes SecurityLevel;
public Dictionary<uint, byte> CharacterCounts;
public Dictionary<string, LastPlayedCharacterInfo> LastPlayedCharacters;
public GameAccountInfo(SQLFields fields, int startColumn)
{
Id = fields.Read<uint>(startColumn + 0);
Name = fields.Read<string>(startColumn + 1);
UnbanDate = fields.Read<uint>(startColumn + 2);
IsPermanenetlyBanned = fields.Read<uint>(startColumn + 3) != 0;
IsBanned = IsPermanenetlyBanned || UnbanDate > Time.UnixTime;
SecurityLevel = (AccountTypes)fields.Read<byte>(startColumn + 4);
int hashPos = Name.IndexOf('#');
if (hashPos != -1)
DisplayName = "WoW" + Name.Substring(hashPos + 1);
else
DisplayName = Name;
CharacterCounts = new Dictionary<uint, byte>();
LastPlayedCharacters = new Dictionary<string, LastPlayedCharacterInfo>();
}
}
public class LastPlayedCharacterInfo
{
public RealmId RealmId;
public string CharacterName;
public ulong CharacterGUID;
public uint LastPlayedTime;
}
}