Updated Bnet Server.

Port From (https://github.com/TrinityCore/TrinityCore)
This commit is contained in:
hondacrx
2024-02-21 00:05:48 -05:00
parent 7960e7b192
commit 9e3a7df6a7
62 changed files with 2186 additions and 731 deletions
-174
View File
@@ -1,174 +0,0 @@
// 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 Framework.Configuration;
using Framework.Constants;
using Framework.Database;
using Framework.Networking;
using Framework.Serialization;
using Framework.Web;
using System;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
namespace BNetServer.Networking
{
public class RestSession : SSLSocket
{
public RestSession(Socket socket) : base(socket) { }
public override void Accept()
{
AsyncHandshake(Global.LoginServiceMgr.GetCertificate());
}
public async override void ReadHandler(byte[] data, int receivedLength)
{
var httpRequest = HttpHelper.ParseRequest(data, receivedLength);
if (httpRequest == null)
return;
switch (httpRequest.Method)
{
case "GET":
default:
SendResponse(HttpCode.Ok, Global.LoginServiceMgr.GetFormInput());
break;
case "POST":
HandleLoginRequest(httpRequest);
return;
}
await AsyncRead();
}
public void HandleLoginRequest(HttpHeader request)
{
LogonData loginForm = Json.CreateObject<LogonData>(request.Content);
LogonResult loginResult = new();
if (loginForm == null)
{
loginResult.AuthenticationState = "LOGIN";
loginResult.ErrorCode = "UNABLE_TO_DECODE";
loginResult.ErrorMessage = "There was an internal error while connecting to Battle.net. Please try again later.";
SendResponse(HttpCode.BadRequest, loginResult);
return;
}
string login = "";
string password = "";
for (int i = 0; i < loginForm.Inputs.Count; ++i)
{
switch (loginForm.Inputs[i].Id)
{
case "account_name":
login = loginForm.Inputs[i].Value;
break;
case "password":
password = loginForm.Inputs[i].Value;
break;
}
}
PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SelBnetAuthentication);
stmt.AddValue(0, login);
SQLResult result = DB.Login.Query(stmt);
if (!result.IsEmpty())
{
uint accountId = result.Read<uint>(0);
string pass_hash = result.Read<string>(1);
uint failedLogins = result.Read<uint>(2);
string loginTicket = result.Read<string>(3);
uint loginTicketExpiry = result.Read<uint>(4);
bool isBanned = result.Read<ulong>(5) != 0;
if (CalculateShaPassHash(login.ToUpper(), password.ToUpper()) == pass_hash)
{
if (loginTicket.IsEmpty() || loginTicketExpiry < Time.UnixTime)
{
byte[] ticket = Array.Empty<byte>().GenerateRandomKey(20);
loginTicket = "TC-" + ticket.ToHexString();
}
stmt = LoginDatabase.GetPreparedStatement(LoginStatements.UpdBnetAuthentication);
stmt.AddValue(0, loginTicket);
stmt.AddValue(1, Time.UnixTime + 3600);
stmt.AddValue(2, accountId);
DB.Login.Execute(stmt);
loginResult.LoginTicket = loginTicket;
}
else if (!isBanned)
{
uint maxWrongPassword = ConfigMgr.GetDefaultValue("WrongPass.MaxCount", 0u);
if (ConfigMgr.GetDefaultValue("WrongPass.Logging", false))
Log.outDebug(LogFilter.Network, $"[{request.Host}, Account {login}, Id {accountId}] Attempted to connect with wrong password!");
if (maxWrongPassword != 0)
{
SQLTransaction trans = new();
stmt = LoginDatabase.GetPreparedStatement(LoginStatements.UpdBnetFailedLogins);
stmt.AddValue(0, accountId);
trans.Append(stmt);
++failedLogins;
Log.outDebug(LogFilter.Network, $"MaxWrongPass : {maxWrongPassword}, failed_login : {accountId}");
if (failedLogins >= maxWrongPassword)
{
BanMode banType = ConfigMgr.GetDefaultValue("WrongPass.BanType", BanMode.IP);
int banTime = ConfigMgr.GetDefaultValue("WrongPass.BanTime", 600);
if (banType == BanMode.Account)
{
stmt = LoginDatabase.GetPreparedStatement(LoginStatements.InsBnetAccountAutoBanned);
stmt.AddValue(0, accountId);
}
else
{
stmt = LoginDatabase.GetPreparedStatement(LoginStatements.InsIpAutoBanned);
stmt.AddValue(0, request.Host);
}
stmt.AddValue(1, banTime);
trans.Append(stmt);
stmt = LoginDatabase.GetPreparedStatement(LoginStatements.UpdBnetResetFailedLogins);
stmt.AddValue(0, accountId);
trans.Append(stmt);
}
DB.Login.CommitTransaction(trans);
}
}
loginResult.AuthenticationState = "DONE";
SendResponse(HttpCode.Ok, loginResult);
}
else
{
loginResult.AuthenticationState = "LOGIN";
loginResult.ErrorCode = "UNABLE_TO_DECODE";
loginResult.ErrorMessage = "There was an internal error while connecting to Battle.net. Please try again later.";
SendResponse(HttpCode.BadRequest, loginResult);
}
}
async void SendResponse<T>(HttpCode code, T response)
{
await AsyncWrite(HttpHelper.CreateResponse(code, Json.CreateString(response)));
}
string CalculateShaPassHash(string name, string password)
{
SHA256 sha256 = SHA256.Create();
var email = sha256.ComputeHash(Encoding.UTF8.GetBytes(name));
return sha256.ComputeHash(Encoding.UTF8.GetBytes(email.ToHexString() + ":" + password)).ToHexString(true);
}
}
}
@@ -39,11 +39,9 @@ namespace BNetServer.Networking
os = logonRequest.Platform;
build = (uint)logonRequest.ApplicationVersion;
var hostname = Global.LoginServiceMgr.GetHostnameForClient(GetRemoteIpEndPoint());
ChallengeExternalRequest externalChallenge = new();
externalChallenge.PayloadType = "web_auth_url";
externalChallenge.Payload = ByteString.CopyFromUtf8($"https://{Global.LoginServiceMgr.GetHostnameForClient(GetRemoteIpEndPoint())}:{Global.LoginServiceMgr.GetPort()}/bnetserver/login/");
externalChallenge.Payload = ByteString.CopyFromUtf8($"https://{Global.LoginService.GetHostnameForClient(GetRemoteIpAddress())}:{Global.LoginService.GetPort()}/bnetserver/login/");
SendRequest((uint)OriginalHash.ChallengeListener, 3, externalChallenge);
return BattlenetRpcErrorCode.Ok;
@@ -55,7 +53,7 @@ namespace BNetServer.Networking
if (verifyWebCredentialsRequest.WebCredentials.IsEmpty)
return BattlenetRpcErrorCode.Denied;
PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SelBnetAccountInfo);
PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SEL_BNET_ACCOUNT_INFO);
stmt.AddValue(0, verifyWebCredentialsRequest.WebCredentials.ToStringUtf8());
SQLResult result = DB.Login.Query(stmt);
@@ -81,7 +79,7 @@ namespace BNetServer.Networking
} while (characterCountsResult.NextRow());
}
stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SelBnetLastPlayerCharacters);
stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SEL_BNET_LAST_PLAYER_CHARACTERS);
stmt.AddValue(0, accountInfo.Id);
SQLResult lastPlayerCharactersResult = DB.Login.Query(stmt);
@@ -102,7 +100,7 @@ namespace BNetServer.Networking
} while (lastPlayerCharactersResult.NextRow());
}
string ip_address = GetRemoteIpEndPoint().ToString();
string ip_address = GetRemoteIpAddress().ToString();
// If the IP is 'locked', check that the player comes indeed from the correct IP address
if (accountInfo.IsLockedToIP)
@@ -5,11 +5,14 @@ using Bgs.Protocol;
using Bgs.Protocol.GameUtilities.V1;
using Framework.Constants;
using Framework.Database;
using Framework.Serialization;
using Framework.IO;
using Framework.Web;
using Framework.Web.Rest.Realmlist;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
namespace BNetServer.Networking
{
@@ -36,7 +39,7 @@ namespace BNetServer.Networking
{
Bgs.Protocol.Attribute attr = request.Attribute[i];
if (attr.Name.Contains("Command_"))
{
{
command = attr;
Params[removeSuffix(attr.Name)] = attr.Value;
}
@@ -80,10 +83,15 @@ namespace BNetServer.Networking
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;
string jsonString = identity.BlobValue.ToStringUtf8().TrimEnd('\0');
int jsonStart = jsonString.IndexOf(':');
if (jsonStart != -1)
{
var realmListTicketIdentity = JsonSerializer.Deserialize<RealmListTicketIdentity>(jsonString.Substring(jsonStart + 1));
var gameAccount = accountInfo.GameAccounts.LookupByKey(realmListTicketIdentity.GameAccountId);
if (gameAccount != null)
gameAccountInfo = gameAccount;
}
}
if (gameAccountInfo == null)
@@ -98,18 +106,23 @@ namespace BNetServer.Networking
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;
string jsonString = clientInfo.BlobValue.ToStringUtf8().Trim('\0');
int jsonStart = jsonString.IndexOf(':');
if (jsonStart != -1)
{
var realmListTicketClientInformation = JsonSerializer.Deserialize<RealmListTicketClientInformation>(jsonString.Substring(jsonStart + 1));
clientInfoOk = true;
int i = 0;
foreach (byte b in realmListTicketClientInformation.Info.Secret)
clientSecret[i++] = b;
}
}
if (!clientInfoOk)
return BattlenetRpcErrorCode.WowServicesDeniedRealmListTicket;
PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.UpdBnetLastLoginInfo);
stmt.AddValue(0, GetRemoteIpEndPoint().ToString());
PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.UPD_BNET_LAST_LOGIN_INFO);
stmt.AddValue(0, GetRemoteIpAddress().ToString());
stmt.AddValue(1, (byte)locale.ToEnum<Locale>());
stmt.AddValue(2, os);
stmt.AddValue(3, accountInfo.Id);
@@ -119,7 +132,7 @@ namespace BNetServer.Networking
var attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_RealmListTicket";
attribute.Value = new Variant();
attribute.Value.BlobValue = ByteString.CopyFrom("AuthRealmListTicket", System.Text.Encoding.UTF8);
attribute.Value.BlobValue = ByteString.CopyFrom("AuthRealmListTicket", Encoding.UTF8);
response.Attribute.Add(attribute);
return BattlenetRpcErrorCode.Ok;
@@ -197,7 +210,10 @@ namespace BNetServer.Networking
realmCharacterCounts.Counts.Add(countEntry);
}
compressed = Json.Deflate("JSONRealmCharacterCountList", realmCharacterCounts);
var jsonData = Encoding.UTF8.GetBytes("JSONRealmCharacterCountList:" + JsonSerializer.Serialize(realmCharacterCounts) + "\0");
var compressedData = ZLib.Compress(jsonData);
compressed = BitConverter.GetBytes(jsonData.Length).Combine(compressedData);
attribute = new Bgs.Protocol.Attribute();
attribute.Name = "Param_CharacterCountList";
@@ -211,9 +227,9 @@ namespace BNetServer.Networking
{
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 Global.RealmMgr.JoinRealm((uint)realmAddress.UintValue, build, GetRemoteIpAddress(), clientSecret, (Locale)Enum.Parse(typeof(Locale), locale), os, _timezoneOffset, gameAccountInfo.Name, response);
return BattlenetRpcErrorCode.WowServicesInvalidJoinTicket;
}
}
}
}
+25 -11
View File
@@ -12,10 +12,11 @@ using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace BNetServer.Networking
{
partial class Session : SSLSocket
public partial class Session : SSLSocket
{
AccountInfo accountInfo;
GameAccountInfo gameAccountInfo;
@@ -23,6 +24,7 @@ namespace BNetServer.Networking
string locale;
string os;
uint build;
TimeSpan _timezoneOffset;
string ipCountry;
byte[] clientSecret;
@@ -39,19 +41,19 @@ namespace BNetServer.Networking
responseCallbacks = new Dictionary<uint, Action<CodedInputStream>>();
}
public override void Accept()
public override void Start()
{
string ipAddress = GetRemoteIpEndPoint().ToString();
string ipAddress = GetRemoteIpAddress().ToString();
Log.outInfo(LogFilter.Network, $"{GetClientInfo()} Connection Accepted.");
// Verify that this IP is not in the ip_banned table
DB.Login.Execute(LoginDatabase.GetPreparedStatement(LoginStatements.DelExpiredIpBans));
DB.Login.Execute(LoginDatabase.GetPreparedStatement(LoginStatements.DEL_EXPIRED_IP_BANS));
PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SelIpInfo);
PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SEL_IP_INFO);
stmt.AddValue(0, ipAddress);
stmt.AddValue(1, BitConverter.ToUInt32(GetRemoteIpEndPoint().Address.GetAddressBytes(), 0));
stmt.AddValue(1, BitConverter.ToUInt32(GetRemoteIpAddress().GetAddressBytes(), 0));
queryProcessor.AddCallback(DB.Login.AsyncQuery(stmt).WithCallback(async result =>
queryProcessor.AddCallback(DB.Login.AsyncQuery(stmt).WithCallback(async result =>
{
if (!result.IsEmpty())
{
@@ -78,6 +80,18 @@ namespace BNetServer.Networking
}));
}
public async override Task HandshakeHandler(Exception ex = null)
{
if (ex != null)
{
Log.outError(LogFilter.Session, $"{GetClientInfo()} SSL Handshake failed {ex.Message}");
CloseSocket();
return;
}
await AsyncRead();
}
public override bool Update()
{
if (!base.Update())
@@ -103,7 +117,7 @@ namespace BNetServer.Networking
header.MergeFrom(data, readPos, headerLength);
readPos += headerLength;
var stream = new CodedInputStream(data, readPos, (int)header.Size);
var stream = new CodedInputStream(data, readPos, (int)header.Size);
readPos += (int)header.Size;
if (header.ServiceId != 0xFE && header.ServiceHash != 0)
@@ -181,7 +195,7 @@ namespace BNetServer.Networking
{
var size = (ushort)header.CalculateSize();
byte[] bytes = new byte[2];
bytes[0] = (byte)((size >> 8) & 0xff);
bytes[0] = (byte)(size >> 8 & 0xff);
bytes[1] = (byte)(size & 0xff);
var headerSizeBytes = BitConverter.GetBytes((ushort)header.CalculateSize());
@@ -192,7 +206,7 @@ namespace BNetServer.Networking
public string GetClientInfo()
{
string stream = '[' + GetRemoteIpEndPoint().ToString();
string stream = '[' + GetRemoteIpAddress().ToString();
if (accountInfo != null && !accountInfo.Login.IsEmpty())
stream += ", Account: " + accountInfo.Login;
@@ -206,7 +220,7 @@ namespace BNetServer.Networking
}
public class AccountInfo
{
{
public uint Id;
public string Login;
public bool IsLockedToIP;
@@ -0,0 +1,27 @@
// 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 Framework.Networking;
using System.Net.Sockets;
namespace BNetServer.Networking
{
public class SessionManager : SocketManager<Session>
{
public static SessionManager Instance { get; } = new SessionManager();
public override bool StartNetwork(string bindIp, int port, int threadCount = 1)
{
if (!base.StartNetwork(bindIp, port, threadCount))
return false;
Acceptor.AsyncAcceptSocket(OnSocketAccept);
return true;
}
static void OnSocketAccept(Socket sock)
{
Global.SessionMgr.OnSocketOpen(sock);
}
}
}