38#include "attribute_types.pb.h"
42#include <rapidjson/document.h>
48 Field* fields = result->Fetch();
58 static constexpr uint32 GameAccountFieldsOffset = 8;
62 GameAccounts[result->Fetch()[GameAccountFieldsOffset].GetUInt32()].LoadResult(result->Fetch() + GameAccountFieldsOffset);
64 }
while (result->NextRow());
73 IsPermanenetlyBanned = fields[3].
GetUInt32() != 0;
74 IsBanned = IsPermanenetlyBanned || UnbanDate > time(
nullptr);
77 std::size_t hashPos =
Name.find(
'#');
78 if (hashPos != std::string::npos)
79 DisplayName = std::string(
"WoW") +
Name.substr(hashPos + 1);
100 TC_LOG_TRACE(
"session",
"{} Accepted connection", GetClientInfo());
103 std::array<std::shared_ptr<Trinity::Net::SocketConnectionInitializer>, 3> initializers =
105 std::make_shared<Trinity::Net::IpBanCheckConnectionInitializer<Session>>(
this),
115 if (!_socket->Update())
118 _queryProcessor.ProcessReadyCallbacks();
125 if (!_socket->IsOpen())
128 _socket->QueuePacket(std::move(*packet));
136 header.
set_size(response->ByteSize());
138 uint16 headerSize = header.ByteSize();
142 packet.
Write(&headerSize,
sizeof(headerSize));
148 response->SerializeToArray(ptr, response->GetCachedSize());
160 uint16 headerSize = header.ByteSize();
164 packet.
Write(&headerSize,
sizeof(headerSize));
178 header.
set_size(request->ByteSize());
181 uint16 headerSize = header.ByteSize();
185 packet.
Write(&headerSize,
sizeof(headerSize));
191 request->SerializeToArray(ptr, request->GetCachedSize());
198 _queryProcessor.AddCallback(std::move(queryCallback));
203 if (logonRequest->program() !=
"WoW")
205 TC_LOG_DEBUG(
"session",
"[Battlenet::LogonRequest] {} attempted to log in with game other than WoW (using {})!", GetClientInfo(), logonRequest->program());
211 TC_LOG_DEBUG(
"session",
"[Battlenet::LogonRequest] {} attempted to log in from an unsupported platform (using {})!", GetClientInfo(), logonRequest->platform());
217 TC_LOG_DEBUG(
"session",
"[Battlenet::LogonRequest] {} attempted to log in with unsupported locale (using {})!", GetClientInfo(), logonRequest->locale());
221 _locale = logonRequest->locale();
222 _os = logonRequest->platform();
223 _build = logonRequest->application_version();
225 _timezoneOffset = [&]
227 if (!logonRequest->has_device_id())
230 rapidjson::Document doc;
231 doc.Parse(logonRequest->device_id().c_str(), logonRequest->device_id().length());
232 if (doc.HasParseError())
235 auto itr = doc.FindMember(
"UTCO");
236 if (itr == doc.MemberEnd())
239 if (!itr->value.IsUint())
245 if (logonRequest->has_cached_web_credentials())
246 return VerifyWebCredentials(logonRequest->cached_web_credentials(), continuation);
258 if (verifyWebCredentialsRequest->has_web_credentials())
259 return VerifyWebCredentials(verifyWebCredentialsRequest->web_credentials(), continuation);
269 switch (request->program())
276 auto asPrintable = [](
char c) {
return std::isprint(c) ? c :
' '; };
278 TC_LOG_DEBUG(
"session",
"[Battlenet::HandleGenerateWebCredentials] {} attempted to generate web cretentials with game other than WoW (using {}{}{}{})!",
279 GetClientInfo(), asPrintable((request->program() >> 24) & 0xFF), asPrintable((request->program() >> 16) & 0xFF),
280 asPrintable((request->program() >> 8) & 0xFF), asPrintable(request->program() & 0xFF));
291 Battlenet::Services::Authentication asyncContinuationService(this);
292 authentication::v1::GenerateWebCredentialsResponse response;
293 response.set_web_credentials((*result)[0].GetCString());
294 asyncContinuation(&asyncContinuationService, ERROR_OK, &response);
302 if (webCredentials.empty())
308 std::function<void(
ServiceBase*,
uint32, ::google::protobuf::Message
const*)> asyncContinuation = std::move(continuation);
309 std::shared_ptr<AccountInfo> accountInfo = std::make_shared<AccountInfo>();
312 Battlenet::Services::Authentication asyncContinuationService(this);
316 asyncContinuation(&asyncContinuationService, ERROR_DENIED, &response);
320 accountInfo->LoadResult(result);
322 if (accountInfo->LoginTicketExpiry < time(
nullptr))
324 asyncContinuation(&asyncContinuationService,
ERROR_TIMED_OUT, &response);
334 if (characterCountsResult)
338 Field* fields = characterCountsResult->Fetch();
339 accountInfo->GameAccounts[fields[0].
GetUInt32()]
342 }
while (characterCountsResult->NextRow());
349 .WithPreparedCallback([
this, accountInfo, asyncContinuation](
PreparedQueryResult lastPlayerCharactersResult)
351 if (lastPlayerCharactersResult)
355 Field* fields = lastPlayerCharactersResult->Fetch();
358 .LastPlayedCharacters[realmId.GetSubRegionAddress()];
360 lastPlayedCharacter.
RealmId = realmId;
365 }
while (lastPlayerCharactersResult->NextRow());
368 _accountInfo = accountInfo;
372 std::string ip_address = _socket->GetRemoteIpAddress().to_string();
375 if (_accountInfo->IsLockedToIP)
377 TC_LOG_DEBUG(
"session",
"[Session::HandleVerifyWebCredentials] Account '{}' is locked to IP - '{}' is logging in from '{}'",
378 _accountInfo->Login, _accountInfo->LastIP, ip_address);
380 if (_accountInfo->LastIP != ip_address)
389 _ipCountry = location->CountryCode;
391 TC_LOG_DEBUG(
"session",
"[Session::HandleVerifyWebCredentials] Account '{}' is not locked to ip", _accountInfo->Login);
392 if (_accountInfo->LockCountry.empty() || _accountInfo->LockCountry ==
"00")
393 TC_LOG_DEBUG(
"session",
"[Session::HandleVerifyWebCredentials] Account '{}' is not locked to country", _accountInfo->Login);
394 else if (!_accountInfo->LockCountry.empty() && !_ipCountry.empty())
396 TC_LOG_DEBUG(
"session",
"[Session::HandleVerifyWebCredentials] Account '{}' is locked to country: '{}' Player country is '{}'",
397 _accountInfo->Login, _accountInfo->LockCountry, _ipCountry);
399 if (_ipCountry != _accountInfo->LockCountry)
408 if (_accountInfo->IsBanned)
410 if (_accountInfo->IsPermanenetlyBanned)
412 TC_LOG_DEBUG(
"session",
"{} [Session::HandleVerifyWebCredentials] Banned account {} tried to login!", GetClientInfo(), _accountInfo->Login);
418 TC_LOG_DEBUG(
"session",
"{} [Session::HandleVerifyWebCredentials] Temporarily banned account {} tried to login!", GetClientInfo(), _accountInfo->Login);
424 authentication::v1::LogonResult logonResult;
425 logonResult.set_error_code(0);
426 logonResult.mutable_account_id()->set_low(_accountInfo->Id);
427 logonResult.mutable_account_id()->set_high(
UI64LIT(0x100000000000000));
428 for (
auto const& [
id, gameAccountInfo] : accountInfo->GameAccounts)
430 EntityId* gameAccountId = logonResult.add_game_account_id();
431 gameAccountId->
set_low(gameAccountInfo.Id);
435 if (!_ipCountry.empty())
436 logonResult.set_geoip_country(_ipCountry);
438 std::array<uint8, 64> k = Trinity::Crypto::GetRandomBytes<64>();
439 logonResult.set_session_key(k.data(), 64);
443 asyncContinuation(&asyncContinuationService,
ERROR_OK, &response);
444 Service<authentication::v1::AuthenticationListener>(
this).OnLogonComplete(&logonResult);
455 if (request->options().field_privacy_info())
457 response->mutable_state()->mutable_privacy_info()->set_is_using_rid(
false);
458 response->mutable_state()->mutable_privacy_info()->set_is_visible_for_view_friends(
false);
459 response->mutable_state()->mutable_privacy_info()->set_is_hidden_from_friend_finder(
true);
461 response->mutable_tags()->set_privacy_info_tag(0xD7CA834D);
472 if (request->options().field_game_level_info())
474 auto itr = _accountInfo->GameAccounts.find(request->game_account_id().low());
475 if (itr != _accountInfo->GameAccounts.end())
477 response->mutable_state()->mutable_game_level_info()->set_name(itr->second.DisplayName);
478 response->mutable_state()->mutable_game_level_info()->set_program(5730135);
481 response->mutable_tags()->set_game_level_info_tag(0x5C46D483);
484 if (request->options().field_game_status())
486 auto itr = _accountInfo->GameAccounts.find(request->game_account_id().low());
487 if (itr != _accountInfo->GameAccounts.end())
489 response->mutable_state()->mutable_game_status()->set_is_suspended(itr->second.IsBanned);
490 response->mutable_state()->mutable_game_status()->set_is_banned(itr->second.IsPermanenetlyBanned);
491 response->mutable_state()->mutable_game_status()->set_suspension_expires(
uint64(itr->second.UnbanDate) * 1000000);
494 response->mutable_state()->mutable_game_status()->set_program(5730135);
495 response->mutable_tags()->set_game_status_tag(0x98B75F99);
515 std::unordered_map<std::string, Variant const*>
params;
516 auto removeSuffix = [](std::string
const& string) -> std::string
518 size_t pos =
string.rfind(
'_');
519 if (pos != std::string::npos)
520 return string.substr(0, pos);
525 for (
int32 i = 0; i < request->attribute_size(); ++i)
527 Attribute const& attr = request->attribute(i);
528 if (strstr(attr.
name().c_str(),
"Command_") == attr.
name().c_str())
539 TC_LOG_ERROR(
"session.rpc",
"{} sent ClientRequest with no command.", GetClientInfo());
543 auto itr = ClientRequestHandlers.find(removeSuffix(command->
name()));
544 if (itr == ClientRequestHandlers.end())
546 TC_LOG_ERROR(
"session.rpc",
"{} sent ClientRequest with unknown command {}.", GetClientInfo(), removeSuffix(command->
name()));
550 return (this->*itr->second)(
params, response);
558 std::size_t jsonStart = identity->blob_value().find(
':');
559 if (jsonStart != std::string::npos &&
::JSON::Deserialize(identity->blob_value().substr(jsonStart + 1), &data))
561 auto itr = _accountInfo->GameAccounts.find(data.
gameaccountid());
562 if (itr != _accountInfo->GameAccounts.end())
563 _gameAccountInfo = &itr->second;
567 if (!_gameAccountInfo)
570 if (_gameAccountInfo->IsPermanenetlyBanned)
572 else if (_gameAccountInfo->IsBanned)
575 bool clientInfoOk =
false;
579 std::size_t jsonStart = clientInfo->blob_value().find(
':');
580 if (jsonStart != std::string::npos &&
::JSON::Deserialize(clientInfo->blob_value().substr(jsonStart + 1), &data))
582 if (_clientSecret.size() == data.
info().
secret().size())
585 memcpy(_clientSecret.data(), data.
info().
secret().data(), _clientSecret.size());
596 stmt->
setString(0, _socket->GetRemoteIpAddress().to_string());
603 Attribute* attribute = response->add_attribute();
604 attribute->
set_name(
"Param_RealmListTicket");
614 auto lastPlayerChar = _gameAccountInfo->LastPlayedCharacters.find(subRegion->string_value());
615 if (lastPlayerChar != _gameAccountInfo->LastPlayedCharacters.end())
617 std::vector<uint8> compressed =
sRealmList->GetRealmEntryJSON(lastPlayerChar->second.RealmId, _build, _gameAccountInfo->SecurityLevel);
619 if (compressed.empty())
622 Attribute* attribute = response->add_attribute();
623 attribute->
set_name(
"Param_RealmEntry");
626 attribute = response->add_attribute();
627 attribute->
set_name(
"Param_CharacterName");
630 attribute = response->add_attribute();
631 attribute->
set_name(
"Param_CharacterGUID");
634 attribute = response->add_attribute();
635 attribute->
set_name(
"Param_LastPlayedTime");
647 if (!_gameAccountInfo)
650 std::string subRegionId;
652 subRegionId = subRegion->string_value();
654 std::vector<uint8> compressed =
sRealmList->GetRealmList(_build, _gameAccountInfo->SecurityLevel, subRegionId);
656 if (compressed.empty())
659 Attribute* attribute = response->add_attribute();
660 attribute->
set_name(
"Param_RealmList");
664 for (
auto const& characterCount : _gameAccountInfo->CharacterCounts)
668 countEntry->
set_count(characterCount.second);
671 std::string json =
"JSONRealmCharacterCountList:" +
::JSON::Serialize(realmCharacterCounts);
673 uLongf compressedLength = compressBound(json.length());
674 compressed.resize(4 + compressedLength);
675 *
reinterpret_cast<uint32*
>(compressed.data()) = json.length() + 1;
677 if (compress(compressed.data() + 4, &compressedLength,
reinterpret_cast<uint8 const*
>(json.c_str()), json.length() + 1) != Z_OK)
680 attribute = response->add_attribute();
681 attribute->
set_name(
"Param_CharacterCountList");
689 return sRealmList->JoinRealm(realmAddress->uint_value(), _build, _clientInfo, _socket->GetRemoteIpAddress(), _clientSecret,
GetLocaleByName(_locale),
690 _os, _timezoneOffset, _gameAccountInfo->Name, _gameAccountInfo->SecurityLevel, response);
700 if (request->attribute_key().find(
"Command_RealmListRequest_v1") == 0)
709template<
bool(Battlenet::Session::*processMethod)(), MessageBuffer Battlenet::Session::*outputBuffer>
733 if (!(session->*processMethod)())
748 return *partialResult;
751 return *partialResult;
754 return *partialResult;
756 _headerLengthBuffer.
Reset();
757 _headerBuffer.Reset();
765 uint16 len = *
reinterpret_cast<uint16*
>(_headerLengthBuffer.GetReadPointer());
767 _headerBuffer.Resize(len);
774 if (!header.ParseFromArray(_headerBuffer.GetReadPointer(), _headerBuffer.GetActiveSize()))
777 _packetBuffer.Resize(header.
size());
784 bool parseSuccess = header.ParseFromArray(_headerBuffer.GetReadPointer(), _headerBuffer.GetActiveSize());
793 auto itr = _responseCallbacks.find(header.
token());
794 if (itr != _responseCallbacks.end())
796 itr->second(std::move(_packetBuffer));
797 _responseCallbacks.erase(header.
token());
800 _packetBuffer.Reset();
808 std::ostringstream stream;
809 stream <<
'[' << _socket->GetRemoteIpAddress() <<
':' << _socket->GetRemotePort();
810 if (_accountInfo && !_accountInfo->Login.empty())
811 stream <<
", Account: " << _accountInfo->Login;
813 if (_gameAccountInfo)
814 stream <<
", Game account: " << _gameAccountInfo->Name;
818 return std::move(stream).str();
@ ERROR_GAME_ACCOUNT_BANNED
@ ERROR_RISK_ACCOUNT_LOCKED
@ ERROR_RPC_NOT_IMPLEMENTED
@ ERROR_RPC_MALFORMED_REQUEST
@ ERROR_WOW_SERVICES_INVALID_JOIN_TICKET
@ ERROR_UTIL_SERVER_INVALID_IDENTITY_ARGS
@ ERROR_UTIL_SERVER_FAILED_TO_SERIALIZE_RESPONSE
@ ERROR_USER_SERVER_BAD_WOW_ACCOUNT
@ ERROR_GAME_ACCOUNT_SUSPENDED
@ ERROR_WOW_SERVICES_DENIED_REALM_LIST_TICKET
@ ERROR_UTIL_SERVER_UNKNOWN_REALM
void EndianConvertReverse(T &)
LocaleConstant GetLocaleByName(std::string_view name)
constexpr bool IsValidLocale(LocaleConstant locale)
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
std::unordered_set< uint32 > params[2]
#define TC_LOG_DEBUG(filterType__, message__,...)
#define TC_LOG_ERROR(filterType__, message__,...)
#define TC_LOG_TRACE(filterType__, message__,...)
@ LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID
@ LOGIN_UPD_BNET_LAST_LOGIN_INFO
@ LOGIN_SEL_BNET_EXISTING_AUTHENTICATION_BY_ID
@ LOGIN_SEL_BNET_ACCOUNT_INFO
@ LOGIN_SEL_BNET_LAST_PLAYER_CHARACTERS
std::optional< T > Optional
Optional helper class to wrap optional values within.
#define sServiceDispatcher
static Optional< Trinity::Net::SocketReadCallbackResult > PartialProcessPacket(Battlenet::Session *session, MessageBuffer &inputBuffer)
uint32 VerifyWebCredentials(std::string const &webCredentials, std::function< void(ServiceBase *, uint32, ::google::protobuf::Message const *)> &continuation)
uint32 JoinRealm(std::unordered_map< std::string, Variant const * > const ¶ms, game_utilities::v1::ClientResponse *response)
std::string GetClientInfo() const
ClientBuild::VariantId _clientInfo
uint32 HandleGenerateWebCredentials(authentication::v1::GenerateWebCredentialsRequest const *request, std::function< void(ServiceBase *, uint32, google::protobuf::Message const *)> &continuation)
static std::unordered_map< std::string, ClientRequestHandler > const ClientRequestHandlers
void SendResponse(uint32 token, pb::Message const *response)
Trinity::Net::SocketReadCallbackResult ReadHandler()
uint32 GetRealmList(std::unordered_map< std::string, Variant const * > const ¶ms, game_utilities::v1::ClientResponse *response)
uint32 HandleProcessClientRequest(game_utilities::v1::ClientRequest const *request, game_utilities::v1::ClientResponse *response)
uint32 HandleVerifyWebCredentials(authentication::v1::VerifyWebCredentialsRequest const *verifyWebCredentialsRequest, std::function< void(ServiceBase *, uint32, ::google::protobuf::Message const *)> &continuation)
uint32 GetLastCharPlayed(std::unordered_map< std::string, Variant const * > const ¶ms, game_utilities::v1::ClientResponse *response)
static std::shared_ptr< Socket > CreateSocket(Trinity::Net::IoContextTcpSocket &&socket)
uint32 HandleGetGameAccountState(account::v1::GetGameAccountStateRequest const *request, account::v1::GetGameAccountStateResponse *response)
Session(Trinity::Net::IoContextTcpSocket &&socket)
bool ReadHeaderLengthHandler()
void AsyncWrite(MessageBuffer *packet)
uint32 HandleGetAllValuesForAttribute(game_utilities::v1::GetAllValuesForAttributeRequest const *request, game_utilities::v1::GetAllValuesForAttributeResponse *response)
std::array< uint8, 32 > _clientSecret
GameAccountInfo * _gameAccountInfo
uint32 HandleLogon(authentication::v1::LogonRequest const *logonRequest, std::function< void(ServiceBase *, uint32, ::google::protobuf::Message const *)> &continuation)
void QueueQuery(QueryCallback &&queryCallback)
void SendRequest(uint32 serviceHash, uint32 methodId, pb::Message const *request, std::function< void(MessageBuffer)> callback)
std::shared_ptr< Socket > _socket
MessageBuffer _headerLengthBuffer
uint32 HandleGetAccountState(account::v1::GetAccountStateRequest const *request, account::v1::GetAccountStateResponse *response)
uint32 GetRealmListTicket(std::unordered_map< std::string, Variant const * > const ¶ms, game_utilities::v1::ClientResponse *response)
std::shared_ptr< AccountInfo > _accountInfo
static bool UsesDevWildcardCertificate()
static boost::asio::ssl::context & instance()
Class used to access individual fields of database query result.
uint64 GetUInt64() const noexcept
bool GetBool() const noexcept
uint32 GetUInt32() const noexcept
uint8 GetUInt8() const noexcept
std::string GetString() const noexcept
void set_wowrealmaddress(::google::protobuf::uint32 value)
void set_count(::google::protobuf::uint32 value)
inline ::JSON::RealmList::RealmCharacterCountEntry * add_counts()
inline ::google::protobuf::uint32 gameaccountid() const
void Resize(size_type bytes)
size_type GetRemainingSpace() const
void ReadCompleted(size_type bytes)
void WriteCompleted(size_type bytes)
size_type GetActiveSize() const
uint8 * GetWritePointer()
void Write(void const *data, std::size_t size)
void setString(uint8 index, std::string &&value)
void setUInt32(uint8 index, uint32 value)
void setUInt8(uint8 index, uint8 value)
void SetNextQuery(QueryCallback &&next)
const ::bgs::protocol::Variant & value() const
const ::std::string & name() const
inline ::bgs::protocol::Variant * mutable_value()
void set_name(const ::std::string &value)
void set_high(::google::protobuf::uint64 value)
void set_low(::google::protobuf::uint64 value)
void set_int_value(::google::protobuf::int64 value)
void set_blob_value(const ::std::string &value)
void set_string_value(const ::std::string &value)
void set_payload_type(const ::std::string &value)
void set_payload(const ::std::string &value)
TC_SHARED_API bool Deserialize(std::string const &json, google::protobuf::Message *message)
TC_SHARED_API std::string Serialize(google::protobuf::Message const &message)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
boost::asio::basic_stream_socket< boost::asio::ip::tcp, boost::asio::io_context::executor_type > IoContextTcpSocket
Minutes GetOffsetByHash(uint32 hash)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
bool IsPermanenetlyBanned
std::unordered_map< uint32, GameAccountInfo > GameAccounts
void LoadResult(PreparedQueryResult result)
void LoadResult(Field const *fields)
std::string CharacterName
Battlenet::RealmHandle RealmId
static std::shared_ptr< SocketConnectionInitializer > & SetupChain(std::span< std::shared_ptr< SocketConnectionInitializer > > initializers)