Refactoring of BNetServer

This commit is contained in:
hondacrx
2020-07-12 00:06:43 -04:00
parent 4164384b72
commit 581d077acd
318 changed files with 2046 additions and 4694 deletions
@@ -0,0 +1,76 @@
// 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.Account.V1;
using Framework.Constants;
using System.Collections.Generic;
namespace BNetServer.Networking
{
public partial class Session
{
[Service(OriginalHash.AccountService, 30)]
BattlenetRpcErrorCode HandleGetAccountState(GetAccountStateRequest request, GetAccountStateResponse response)
{
if (!authed)
return BattlenetRpcErrorCode.Denied;
if (request.Options.FieldPrivacyInfo)
{
response.State = new AccountState();
response.State.PrivacyInfo = new PrivacyInfo();
response.State.PrivacyInfo.IsUsingRid = false;
response.State.PrivacyInfo.IsVisibleForViewFriends = false;
response.State.PrivacyInfo.IsHiddenFromFriendFinder = true;
response.Tags = new AccountFieldTags();
response.Tags.PrivacyInfoTag = 0xD7CA834D;
}
return BattlenetRpcErrorCode.Ok;
}
[Service(OriginalHash.AccountService, 31)]
BattlenetRpcErrorCode HandleGetGameAccountState(GetGameAccountStateRequest request, GetGameAccountStateResponse response)
{
if (!authed)
return BattlenetRpcErrorCode.Denied;
if (request.Options.FieldGameLevelInfo)
{
var gameAccountInfo = accountInfo.GameAccounts.LookupByKey(request.GameAccountId.Low);
if (gameAccountInfo != null)
{
response.State = new GameAccountState();
response.State.GameLevelInfo = new GameLevelInfo();
response.State.GameLevelInfo.Name = gameAccountInfo.DisplayName;
response.State.GameLevelInfo.Program = 5730135; // WoW
}
response.Tags = new GameAccountFieldTags();
response.Tags.GameLevelInfoTag = 0x5C46D483;
}
if (request.Options.FieldGameStatus)
{
if (response.State == null)
response.State = new GameAccountState();
response.State.GameStatus = new GameStatus();
var gameAccountInfo = accountInfo.GameAccounts.LookupByKey(request.GameAccountId.Low);
if (gameAccountInfo != null)
{
response.State.GameStatus.IsSuspended = gameAccountInfo.IsBanned;
response.State.GameStatus.IsBanned = gameAccountInfo.IsPermanenetlyBanned;
response.State.GameStatus.SuspensionExpires = (gameAccountInfo.UnbanDate * 1000000);
}
response.State.GameStatus.Program = 5730135; // WoW
response.Tags.GameStatusTag = 0x98B75F99;
}
return BattlenetRpcErrorCode.Ok;
}
}
}
@@ -0,0 +1,169 @@
// 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 Bgs.Protocol.Authentication.V1;
using Bgs.Protocol.Challenge.V1;
using Framework.Constants;
using Framework.Database;
using Google.Protobuf;
using System;
using Framework.Realm;
using System.Net;
namespace BNetServer.Networking
{
public partial class Session
{
[Service(OriginalHash.AuthenticationService, 1)]
BattlenetRpcErrorCode HandleLogon(LogonRequest logonRequest, NoData response)
{
if (logonRequest.Program != "WoW")
{
Log.outDebug(LogFilter.Session, $"Battlenet.LogonRequest: {GetClientInfo()} attempted to log in with game other than WoW (using {logonRequest.Program})!");
return BattlenetRpcErrorCode.BadProgram;
}
if (logonRequest.Platform != "Win" && logonRequest.Platform != "Wn64" && logonRequest.Platform != "Mc64")
{
Log.outDebug(LogFilter.Session, $"Battlenet.LogonRequest: {GetClientInfo()} attempted to log in from an unsupported platform (using {logonRequest.Platform})!");
return BattlenetRpcErrorCode.BadPlatform;
}
if (logonRequest.Locale.ToEnum<Locale>() == Locale.enUS && logonRequest.Locale != "enUS")
{
Log.outDebug(LogFilter.Session, $"Battlenet.LogonRequest: {GetClientInfo()} attempted to log in with unsupported locale (using {logonRequest.Locale})!");
return BattlenetRpcErrorCode.BadLocale;
}
locale = logonRequest.Locale;
os = logonRequest.Platform;
build = (uint)logonRequest.ApplicationVersion;
var endpoint = Global.LoginServiceMgr.GetAddressForClient(GetRemoteIpEndPoint().Address);
ChallengeExternalRequest externalChallenge = new ChallengeExternalRequest();
externalChallenge.PayloadType = "web_auth_url";
externalChallenge.Payload = ByteString.CopyFromUtf8($"https://{endpoint.Address}:{endpoint.Port}/bnetserver/login/");
SendRequest((uint)OriginalHash.ChallengeListener, 3, externalChallenge);
return BattlenetRpcErrorCode.Ok;
}
[Service(OriginalHash.AuthenticationService, 7)]
BattlenetRpcErrorCode HandleVerifyWebCredentials(VerifyWebCredentialsRequest verifyWebCredentialsRequest)
{
if (verifyWebCredentialsRequest.WebCredentials.IsEmpty)
return BattlenetRpcErrorCode.Denied;
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SelBnetAccountInfo);
stmt.AddValue(0, verifyWebCredentialsRequest.WebCredentials.ToStringUtf8());
SQLResult result = DB.Login.Query(stmt);
if (result.IsEmpty())
return BattlenetRpcErrorCode.Denied;
accountInfo = new AccountInfo(result);
if (accountInfo.LoginTicketExpiry < Time.UnixTime)
return BattlenetRpcErrorCode.TimedOut;
stmt = DB.Login.GetPreparedStatement(LoginStatements.SelBnetCharacterCountsByAccountId);
stmt.AddValue(0, accountInfo.Id);
SQLResult characterCountsResult = DB.Login.Query(stmt);
if (!characterCountsResult.IsEmpty())
{
do
{
var realmId = new RealmId(characterCountsResult.Read<byte>(3), characterCountsResult.Read<byte>(4), characterCountsResult.Read<uint>(2));
accountInfo.GameAccounts[characterCountsResult.Read<uint>(0)].CharacterCounts[realmId.GetAddress()] = characterCountsResult.Read<byte>(1);
} while (characterCountsResult.NextRow());
}
stmt = DB.Login.GetPreparedStatement(LoginStatements.SelBnetLastPlayerCharacters);
stmt.AddValue(0, accountInfo.Id);
SQLResult lastPlayerCharactersResult = DB.Login.Query(stmt);
if (!lastPlayerCharactersResult.IsEmpty())
{
do
{
var realmId = new RealmId(lastPlayerCharactersResult.Read<byte>(1), lastPlayerCharactersResult.Read<byte>(2), lastPlayerCharactersResult.Read<uint>(3));
LastPlayedCharacterInfo lastPlayedCharacter = new LastPlayedCharacterInfo();
lastPlayedCharacter.RealmId = realmId;
lastPlayedCharacter.CharacterName = lastPlayerCharactersResult.Read<string>(4);
lastPlayedCharacter.CharacterGUID = lastPlayerCharactersResult.Read<ulong>(5);
lastPlayedCharacter.LastPlayedTime = lastPlayerCharactersResult.Read<uint>(6);
accountInfo.GameAccounts[lastPlayerCharactersResult.Read<uint>(0)].LastPlayedCharacters[realmId.GetSubRegionAddress()] = lastPlayedCharacter;
} while (lastPlayerCharactersResult.NextRow());
}
string ip_address = GetRemoteIpEndPoint().ToString();
// If the IP is 'locked', check that the player comes indeed from the correct IP address
if (accountInfo.IsLockedToIP)
{
Log.outDebug(LogFilter.Session, $"Session.HandleVerifyWebCredentials: Account: {accountInfo.Login} is locked to IP: {accountInfo.LastIP} is logging in from IP: {ip_address}");
if (accountInfo.LastIP != ip_address)
return BattlenetRpcErrorCode.RiskAccountLocked;
}
else
{
Log.outDebug(LogFilter.Session, $"Session.HandleVerifyWebCredentials: Account: {accountInfo.Login} is not locked to ip");
if (accountInfo.LockCountry.IsEmpty() || accountInfo.LockCountry == "00")
Log.outDebug(LogFilter.Session, $"Session.HandleVerifyWebCredentials: Account: {accountInfo.Login} is not locked to country");
else if (!accountInfo.LockCountry.IsEmpty() && !ipCountry.IsEmpty())
{
Log.outDebug(LogFilter.Session, $"Session.HandleVerifyWebCredentials: Account: {accountInfo.Login} is locked to Country: {accountInfo.LockCountry} player Country: {ipCountry}");
if (ipCountry != accountInfo.LockCountry)
return BattlenetRpcErrorCode.RiskAccountLocked;
}
}
// If the account is banned, reject the logon attempt
if (accountInfo.IsBanned)
{
if (accountInfo.IsPermanenetlyBanned)
{
Log.outDebug(LogFilter.Session, $"{GetClientInfo()} Session.HandleVerifyWebCredentials: Banned account {accountInfo.Login} tried to login!");
return BattlenetRpcErrorCode.GameAccountBanned;
}
else
{
Log.outDebug(LogFilter.Session, $"{GetClientInfo()} Session.HandleVerifyWebCredentials: Temporarily banned account {accountInfo.Login} tried to login!");
return BattlenetRpcErrorCode.GameAccountSuspended;
}
}
LogonResult logonResult = new LogonResult();
logonResult.ErrorCode = 0;
logonResult.AccountId = new EntityId();
logonResult.AccountId.Low = accountInfo.Id;
logonResult.AccountId.High = 0x100000000000000;
foreach (var pair in accountInfo.GameAccounts)
{
EntityId gameAccountId = new EntityId();
gameAccountId.Low = pair.Value.Id;
gameAccountId.High = 0x200000200576F57;
logonResult.GameAccountId.Add(gameAccountId);
}
if (!ipCountry.IsEmpty())
logonResult.GeoipCountry = ipCountry;
logonResult.SessionKey = ByteString.CopyFrom(new byte[64].GenerateRandomKey(64));
authed = true;
SendRequest((uint)OriginalHash.AuthenticationListener, 5, logonResult);
return BattlenetRpcErrorCode.Ok;
}
}
}
@@ -0,0 +1,48 @@
// 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.Connection.V1;
using Framework.Constants;
using System.Collections.Generic;
using Google.Protobuf;
using Bgs.Protocol;
using System.Diagnostics;
namespace BNetServer.Networking
{
public partial class Session
{
[Service(OriginalHash.ConnectionService, 1)]
BattlenetRpcErrorCode HandleConnect(ConnectRequest request, ConnectResponse response)
{
if (request.ClientId != null)
response.ClientId.MergeFrom(request.ClientId);
response.ServerId = new ProcessId();
response.ServerId.Label = (uint)Process.GetCurrentProcess().Id;
response.ServerId.Epoch = (uint)Time.UnixTime;
response.ServerTime = (ulong)Time.UnixTimeMilliseconds;
response.UseBindlessRpc = request.UseBindlessRpc;
return BattlenetRpcErrorCode.Ok;
}
[Service(OriginalHash.ConnectionService, 5)]
BattlenetRpcErrorCode HandleKeepAlive(NoData request)
{
return BattlenetRpcErrorCode.Ok;
}
[Service(OriginalHash.ConnectionService, 7)]
BattlenetRpcErrorCode HandleRequestDisconnect(DisconnectRequest request)
{
var disconnectNotification = new DisconnectNotification();
disconnectNotification.ErrorCode = request.ErrorCode;
SendRequest((uint)OriginalHash.ConnectionService, 4, disconnectNotification);
CloseSocket();
return BattlenetRpcErrorCode.Ok;
}
}
}
@@ -0,0 +1,207 @@
// 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 Bgs.Protocol.GameUtilities.V1;
using Framework.Constants;
using Framework.Database;
using Framework.Serialization;
using Framework.Web;
using Google.Protobuf;
using System;
using System.Collections.Generic;
namespace BNetServer.Networking
{
public partial class Session
{
[Service(OriginalHash.GameUtilitiesService, 1)]
BattlenetRpcErrorCode HandleProcessClientRequest(ClientRequest request, ClientResponse response)
{
if (!authed)
return BattlenetRpcErrorCode.Denied;
Bgs.Protocol.Attribute command = null;
Dictionary<string, Variant> Params = new Dictionary<string, Variant>();
for (int i = 0; i < request.Attribute.Count; ++i)
{
Bgs.Protocol.Attribute attr = request.Attribute[i];
Params[attr.Name] = attr.Value;
if (attr.Name.Contains("Command_"))
command = attr;
}
if (command == null)
{
Log.outError(LogFilter.SessionRpc, $"{GetClientInfo()} sent ClientRequest with no command.");
return BattlenetRpcErrorCode.RpcMalformedRequest;
}
return command.Name switch
{
"Command_RealmListTicketRequest_v1_b9" => GetRealmListTicket(Params, response),
"Command_LastCharPlayedRequest_v1_b9" => GetLastCharPlayed(Params, response),
"Command_RealmListRequest_v1_b9" => GetRealmList(Params, response),
"Command_RealmJoinRequest_v1_b9" => JoinRealm(Params, response),
_ => BattlenetRpcErrorCode.RpcNotImplemented
};
}
[Service(OriginalHash.GameUtilitiesService, 10)]
BattlenetRpcErrorCode HandleGetAllValuesForAttribute(GetAllValuesForAttributeRequest request, GetAllValuesForAttributeResponse response)
{
if (!authed)
return BattlenetRpcErrorCode.Denied;
if (request.AttributeKey == "Command_RealmListRequest_v1_b9")
{
Global.RealmMgr.WriteSubRegions(response);
return BattlenetRpcErrorCode.Ok;
}
return BattlenetRpcErrorCode.RpcNotImplemented;
}
BattlenetRpcErrorCode GetRealmListTicket(Dictionary<string, Variant> Params, ClientResponse response)
{
Variant identity = Params.LookupByKey("Param_Identity");
if (identity != null)
{
var realmListTicketIdentity = Json.CreateObject<RealmListTicketIdentity>(identity.BlobValue.ToStringUtf8(), true);
var gameAccount = accountInfo.GameAccounts.LookupByKey(realmListTicketIdentity.GameAccountId);
if (gameAccount != null)
gameAccountInfo = gameAccount;
}
if (gameAccountInfo == null)
return BattlenetRpcErrorCode.UtilServerInvalidIdentityArgs;
if (gameAccountInfo.IsPermanenetlyBanned)
return BattlenetRpcErrorCode.GameAccountBanned;
else if (gameAccountInfo.IsBanned)
return BattlenetRpcErrorCode.GameAccountSuspended;
bool clientInfoOk = false;
Variant clientInfo = Params.LookupByKey("Param_ClientInfo");
if (clientInfo != null)
{
var realmListTicketClientInformation = Json.CreateObject<RealmListTicketClientInformation>(clientInfo.BlobValue.ToStringUtf8(), true);
clientInfoOk = true;
int i = 0;
foreach (byte b in realmListTicketClientInformation.Info.Secret)
clientSecret[i++] = b;
}
if (!clientInfoOk)
return BattlenetRpcErrorCode.WowServicesDeniedRealmListTicket;
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.UpdBnetLastLoginInfo);
stmt.AddValue(0, GetRemoteIpEndPoint().ToString());
stmt.AddValue(1, Enum.Parse(typeof(Locale), locale));
stmt.AddValue(2, os);
stmt.AddValue(3, accountInfo.Id);
DB.Login.Execute(stmt);
var attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_RealmListTicket";
attribute.Value = new Variant();
attribute.Value.BlobValue = ByteString.CopyFrom("AuthRealmListTicket", System.Text.Encoding.UTF8);
response.Attribute.Add(attribute);
return BattlenetRpcErrorCode.Ok;
}
BattlenetRpcErrorCode GetLastCharPlayed(Dictionary<string, Variant> Params, ClientResponse response)
{
Variant subRegion = Params.LookupByKey("Command_LastCharPlayedRequest_v1_b9");
if (subRegion != null)
{
var lastPlayerChar = gameAccountInfo.LastPlayedCharacters.LookupByKey(subRegion.StringValue);
if (lastPlayerChar != null)
{
var compressed = Global.RealmMgr.GetRealmEntryJSON(lastPlayerChar.RealmId, build);
if (compressed.Length == 0)
return BattlenetRpcErrorCode.UtilServerFailedToSerializeResponse;
var attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_RealmEntry";
attribute.Value = new Variant();
attribute.Value.BlobValue = ByteString.CopyFrom(compressed);
response.Attribute.Add(attribute);
attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_CharacterName";
attribute.Value = new Variant();
attribute.Value.StringValue = lastPlayerChar.CharacterName;
response.Attribute.Add(attribute);
attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_CharacterGUID";
attribute.Value = new Variant();
attribute.Value.BlobValue = ByteString.CopyFrom(BitConverter.GetBytes(lastPlayerChar.CharacterGUID));
response.Attribute.Add(attribute);
attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_LastPlayedTime";
attribute.Value = new Variant();
attribute.Value.IntValue = (int)lastPlayerChar.LastPlayedTime;
response.Attribute.Add(attribute);
}
return BattlenetRpcErrorCode.Ok;
}
return BattlenetRpcErrorCode.UtilServerUnknownRealm;
}
BattlenetRpcErrorCode GetRealmList(Dictionary<string, Variant> Params, ClientResponse response)
{
if (gameAccountInfo == null)
return BattlenetRpcErrorCode.UserServerBadWowAccount;
string subRegionId = "";
Variant subRegion = Params.LookupByKey("Command_RealmListRequest_v1_b9");
if (subRegion != null)
subRegionId = subRegion.StringValue;
var compressed = Global.RealmMgr.GetRealmList(build, subRegionId);
if (compressed.Length == 0)
return BattlenetRpcErrorCode.UtilServerFailedToSerializeResponse;
var attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_RealmList";
attribute.Value = new Variant();
attribute.Value.BlobValue = ByteString.CopyFrom(compressed);
response.Attribute.Add(attribute);
var realmCharacterCounts = new RealmCharacterCountList();
foreach (var characterCount in gameAccountInfo.CharacterCounts)
{
var countEntry = new RealmCharacterCountEntry();
countEntry.WowRealmAddress = (int)characterCount.Key;
countEntry.Count = characterCount.Value;
realmCharacterCounts.Counts.Add(countEntry);
}
compressed = Json.Deflate("JSONRealmCharacterCountList", realmCharacterCounts);
attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_CharacterCountList";
attribute.Value = new Variant();
attribute.Value.BlobValue = ByteString.CopyFrom(compressed);
response.Attribute.Add(attribute);
return BattlenetRpcErrorCode.Ok;
}
BattlenetRpcErrorCode JoinRealm(Dictionary<string, Variant> Params, ClientResponse response)
{
Variant realmAddress = Params.LookupByKey("Param_RealmAddress");
if (realmAddress != null)
return Global.RealmMgr.JoinRealm((uint)realmAddress.UintValue, build, GetRemoteIpEndPoint().Address, clientSecret, (Locale)Enum.Parse(typeof(Locale), locale), os, gameAccountInfo.Name, response);
return BattlenetRpcErrorCode.WowServicesInvalidJoinTicket;
}
}
}