diff options
-rw-r--r-- | cmake/macros/ConfigureBoost.cmake | 15 | ||||
-rw-r--r-- | src/server/authserver/Main.cpp | 240 | ||||
-rw-r--r-- | src/server/authserver/Server/AuthServer.cpp | 40 | ||||
-rw-r--r-- | src/server/authserver/Server/AuthServer.h | 44 | ||||
-rw-r--r-- | src/server/authserver/Server/AuthSession.cpp | 915 | ||||
-rw-r--r-- | src/server/authserver/Server/AuthSession.h | 85 | ||||
-rw-r--r-- | src/server/authserver/Server/AuthSocket.cpp | 1207 | ||||
-rw-r--r-- | src/server/authserver/Server/AuthSocket.h | 83 | ||||
-rw-r--r-- | src/server/authserver/Server/RealmAcceptor.h | 70 | ||||
-rw-r--r-- | src/server/authserver/Server/RealmSocket.cpp | 291 | ||||
-rw-r--r-- | src/server/authserver/Server/RealmSocket.h | 80 | ||||
-rw-r--r-- | src/server/worldserver/Master.cpp | 1 |
12 files changed, 1215 insertions, 1856 deletions
diff --git a/cmake/macros/ConfigureBoost.cmake b/cmake/macros/ConfigureBoost.cmake index 320a7cb30d4..c2cd7c43360 100644 --- a/cmake/macros/ConfigureBoost.cmake +++ b/cmake/macros/ConfigureBoost.cmake @@ -1,3 +1,13 @@ +macro(get_WIN32_WINNT version)
+ if (WIN32 AND CMAKE_SYSTEM_VERSION)
+ set(ver ${CMAKE_SYSTEM_VERSION})
+ string(REPLACE "." "" ver ${ver})
+ string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver})
+
+ set(${version} "0x${ver}")
+ endif()
+endmacro()
+
if(WIN32)
set(BOOST_DEBUG ON)
if(DEFINED ENV{BOOST_ROOT})
@@ -12,9 +22,12 @@ if(WIN32) set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
+
+ get_WIN32_WINNT(ver)
+ add_definitions(-D_WIN32_WINNT=${ver})
endif()
-find_package(Boost 1.55 REQUIRED atomic chrono date_time exception system thread)
+find_package(Boost 1.55 REQUIRED atomic chrono date_time exception regex system thread)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 0ccad3d1d8a..c8be23ca524 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -24,11 +24,13 @@ * authentication server */ -#include <ace/Dev_Poll_Reactor.h> -#include <ace/TP_Reactor.h> #include <openssl/opensslv.h> #include <openssl/crypto.h> -#include <boost/asio/signal_set.hpp> +#include <iostream> +#include <cstdlib> +#include <boost/bind.hpp> +#include <boost/asio.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> #include "Common.h" #include "Database/DatabaseEnv.h" @@ -36,9 +38,9 @@ #include "Log.h" #include "SystemConfig.h" #include "Util.h" -#include "SignalHandler.h" #include "RealmList.h" -#include "RealmAcceptor.h" +#include "AuthServer.h" + #ifdef __linux__ #include <sched.h> @@ -52,24 +54,41 @@ bool StartDB(); void StopDB(); +void SetProcessPriority(); -bool stopEvent = false; // Setting it to true stops the server +boost::asio::io_service _ioService; +boost::asio::deadline_timer _dbPingTimer(_ioService); +uint32 _dbPingInterval; LoginDatabaseWorkerPool LoginDatabase; // Accessor to the authserver database -void SignalHandler(const boost::system::error_code& error, int signalNumber)
-{
- TC_LOG_ERROR("server.authserver", "SIGNAL HANDLER WORKING");
- if (!error)
- {
+using boost::asio::ip::tcp; + + +void SignalHandler(const boost::system::error_code& error, int signalNumber) +{ + TC_LOG_ERROR("server.authserver", "SIGNAL HANDLER WORKING"); + if (!error) + { switch (signalNumber) { case SIGINT: case SIGTERM: - stopEvent = true; + _ioService.stop(); break; } - }
+ } +} + +void KeepDatabaseAliveHandler(const boost::system::error_code& error) +{ + if (!error) + { + TC_LOG_INFO("server.authserver", "Ping MySQL to keep connection alive"); + LoginDatabase.KeepAlive(); + + _dbPingTimer.expires_from_now(boost::posix_time::minutes(_dbPingInterval)); + } } /// Print out the usage string for this program on the console. @@ -81,7 +100,7 @@ void usage(const char* prog) } /// Launch the auth server -extern int main(int argc, char** argv) +int main(int argc, char** argv) { // Command line parsing to get the configuration file name char const* configFile = _TRINITY_REALM_CONFIG; @@ -115,14 +134,6 @@ extern int main(int argc, char** argv) TC_LOG_WARN("server.authserver", "%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); -#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) - ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true); -#else - ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true); -#endif - - TC_LOG_DEBUG("server.authserver", "Max allowed open files is %d", ACE::max_handles()); - // authserver PID file creation std::string pidFile = sConfigMgr->GetStringDefault("PidFile", ""); if (!pidFile.empty()) @@ -149,121 +160,32 @@ extern int main(int argc, char** argv) } // Launch the listening network socket - RealmAcceptor acceptor; - int32 rmport = sConfigMgr->GetIntDefault("RealmServerPort", 3724); - if (rmport < 0 || rmport > 0xFFFF) + int32 port = sConfigMgr->GetIntDefault("RealmServerPort", 3724); + if (port < 0 || port > 0xFFFF) { TC_LOG_ERROR("server.authserver", "Specified port out of allowed range (1-65535)"); return 1; } - std::string bind_ip = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); + std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - ACE_INET_Addr bind_addr(uint16(rmport), bind_ip.c_str()); - - if (acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1) - { - TC_LOG_ERROR("server.authserver", "Auth server can not bind to %s:%d", bind_ip.c_str(), rmport); - return 1; - } - - boost::asio::io_service io_service; - - boost::asio::signal_set signals(io_service, SIGINT, SIGTERM); + AuthServer authServer(_ioService, bindIp, port); + // Set signal handlers + boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM); signals.async_wait(SignalHandler); -#if defined(_WIN32) || defined(__linux__) - - ///- Handle affinity for multiple processors and process priority - uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0); - bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false); - -#ifdef _WIN32 // Windows - - HANDLE hProcess = GetCurrentProcess(); - if (affinity > 0) - { - ULONG_PTR appAff; - ULONG_PTR sysAff; - - if (GetProcessAffinityMask(hProcess, &appAff, &sysAff)) - { - // remove non accessible processors - ULONG_PTR currentAffinity = affinity & appAff; - - if (!currentAffinity) - TC_LOG_ERROR("server.authserver", "Processors marked in UseProcessors bitmask (hex) %x are not accessible for the authserver. Accessible processors bitmask (hex): %x", affinity, appAff); - else if (SetProcessAffinityMask(hProcess, currentAffinity)) - TC_LOG_INFO("server.authserver", "Using processors (bitmask, hex): %x", currentAffinity); - else - TC_LOG_ERROR("server.authserver", "Can't set used processors (hex): %x", currentAffinity); - } - } - - if (highPriority) - { - if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) - TC_LOG_INFO("server.authserver", "authserver process priority class set to HIGH"); - else - TC_LOG_ERROR("server.authserver", "Can't set authserver process priority class."); - } - -#else // Linux - - if (affinity > 0) - { - cpu_set_t mask; - CPU_ZERO(&mask); - - for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i) - if (affinity & (1 << i)) - CPU_SET(i, &mask); - - if (sched_setaffinity(0, sizeof(mask), &mask)) - TC_LOG_ERROR("server.authserver", "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno)); - else - { - CPU_ZERO(&mask); - sched_getaffinity(0, sizeof(mask), &mask); - TC_LOG_INFO("server.authserver", "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask)); - } - } - - if (highPriority) - { - if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY)) - TC_LOG_ERROR("server.authserver", "Can't set authserver process priority class, error: %s", strerror(errno)); - else - TC_LOG_INFO("server.authserver", "authserver process priority class set to %i", getpriority(PRIO_PROCESS, 0)); - } - -#endif -#endif + SetProcessPriority(); - // maximum counter for next ping - uint32 numLoops = (sConfigMgr->GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000)); - uint32 loopCounter = 0; - - // Wait for termination signal - while (!stopEvent) - { - // dont move this outside the loop, the reactor will modify it - ACE_Time_Value interval(0, 100000); - if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1) - break; + _dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30); - io_service.run(); + _dbPingTimer.expires_from_now(boost::posix_time::seconds(_dbPingInterval)); + _dbPingTimer.async_wait(KeepDatabaseAliveHandler); - if ((++loopCounter) == numLoops) - { - loopCounter = 0; - TC_LOG_INFO("server.authserver", "Ping MySQL to keep connection alive"); - LoginDatabase.KeepAlive(); - } - } + // Start the io service + _ioService.run(); // Close the Database Pool and library StopDB(); @@ -272,6 +194,7 @@ extern int main(int argc, char** argv) return 0; } + /// Initialize connection to the database bool StartDB() { @@ -316,3 +239,74 @@ void StopDB() LoginDatabase.Close(); MySQL::Library_End(); } + +void SetProcessPriority() +{ +#if defined(_WIN32) || defined(__linux__) + + ///- Handle affinity for multiple processors and process priority + uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0); + bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false); + +#ifdef _WIN32 // Windows + + HANDLE hProcess = GetCurrentProcess(); + if (affinity > 0) + { + ULONG_PTR appAff; + ULONG_PTR sysAff; + + if (GetProcessAffinityMask(hProcess, &appAff, &sysAff)) + { + // remove non accessible processors + ULONG_PTR currentAffinity = affinity & appAff; + + if (!currentAffinity) + TC_LOG_ERROR("server.authserver", "Processors marked in UseProcessors bitmask (hex) %x are not accessible for the authserver. Accessible processors bitmask (hex): %x", affinity, appAff); + else if (SetProcessAffinityMask(hProcess, currentAffinity)) + TC_LOG_INFO("server.authserver", "Using processors (bitmask, hex): %x", currentAffinity); + else + TC_LOG_ERROR("server.authserver", "Can't set used processors (hex): %x", currentAffinity); + } + } + + if (highPriority) + { + if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) + TC_LOG_INFO("server.authserver", "authserver process priority class set to HIGH"); + else + TC_LOG_ERROR("server.authserver", "Can't set authserver process priority class."); + } + +#else // Linux + + if (affinity > 0) + { + cpu_set_t mask; + CPU_ZERO(&mask); + + for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i) + if (affinity & (1 << i)) + CPU_SET(i, &mask); + + if (sched_setaffinity(0, sizeof(mask), &mask)) + TC_LOG_ERROR("server.authserver", "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno)); + else + { + CPU_ZERO(&mask); + sched_getaffinity(0, sizeof(mask), &mask); + TC_LOG_INFO("server.authserver", "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask)); + } + } + + if (highPriority) + { + if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY)) + TC_LOG_ERROR("server.authserver", "Can't set authserver process priority class, error: %s", strerror(errno)); + else + TC_LOG_INFO("server.authserver", "authserver process priority class set to %i", getpriority(PRIO_PROCESS, 0)); + } + +#endif +#endif +}
\ No newline at end of file diff --git a/src/server/authserver/Server/AuthServer.cpp b/src/server/authserver/Server/AuthServer.cpp new file mode 100644 index 00000000000..a699734be79 --- /dev/null +++ b/src/server/authserver/Server/AuthServer.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * 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 2 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/>. + */ + +#include "AuthServer.h" +#include "AuthSession.h" + +#include <boost/bind.hpp> +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +void AuthServer::AsyncAccept() +{ + _acceptor.async_accept(_socket, [this](boost::system::error_code error) + { + if (!error) + { + std::make_shared<AuthSession>(std::move(_socket))->Start(); + } + + AsyncAccept(); + }); +} + + + diff --git a/src/server/authserver/Server/AuthServer.h b/src/server/authserver/Server/AuthServer.h new file mode 100644 index 00000000000..0496326ee7b --- /dev/null +++ b/src/server/authserver/Server/AuthServer.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * 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 2 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/>. + */ + +#ifndef __AUTHSERVER_H__ +#define __AUTHSERVER_H__ + +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +class AuthServer +{ +public: + AuthServer(boost::asio::io_service& ioService, std::string bindIp, int port) : _socket(ioService), _acceptor(ioService, tcp::endpoint(tcp::v4(), port)) + { + tcp::endpoint endpoint(boost::asio::ip::address::from_string(bindIp), port); + + _acceptor = tcp::acceptor(ioService, endpoint); + + AsyncAccept(); + }; + +private: + void AsyncAccept(); + + tcp::acceptor _acceptor; + tcp::socket _socket; +}; + +#endif /* __AUTHSERVER_H__ */ diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp new file mode 100644 index 00000000000..cb78336f2d3 --- /dev/null +++ b/src/server/authserver/Server/AuthSession.cpp @@ -0,0 +1,915 @@ +/* +* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* 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 2 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/>. +*/ + +#include <memory> +#include <boost/asio.hpp> +#include <AuthSession.h> +#include <Log.h> +#include "ByteBuffer.h" +#include "AuthCodes.h" +#include "Database/DatabaseEnv.h" +#include "SHA1.h" +#include "openssl/crypto.h" +#include "Configuration/Config.h" +#include "RealmList.h" + +using boost::asio::ip::tcp; + +enum eAuthCmd +{ + AUTH_LOGON_CHALLENGE = 0x00, + AUTH_LOGON_PROOF = 0x01, + AUTH_RECONNECT_CHALLENGE = 0x02, + AUTH_RECONNECT_PROOF = 0x03, + REALM_LIST = 0x10, + XFER_INITIATE = 0x30, + XFER_DATA = 0x31, + XFER_ACCEPT = 0x32, + XFER_RESUME = 0x33, + XFER_CANCEL = 0x34 +}; + +enum eStatus +{ + STATUS_CONNECTED = 0, + STATUS_AUTHED +}; + +#pragma pack(push, 1) + +typedef struct AUTH_LOGON_CHALLENGE_C +{ + uint8 cmd; + uint8 error; + uint16 size; + uint8 gamename[4]; + uint8 version1; + uint8 version2; + uint8 version3; + uint16 build; + uint8 platform[4]; + uint8 os[4]; + uint8 country[4]; + uint32 timezone_bias; + uint32 ip; + uint8 I_len; + uint8 I[1]; +} sAuthLogonChallenge_C; + +typedef struct AUTH_LOGON_PROOF_C +{ + uint8 cmd; + uint8 A[32]; + uint8 M1[20]; + uint8 crc_hash[20]; + uint8 number_of_keys; + uint8 securityFlags; +} sAuthLogonProof_C; + +typedef struct AUTH_LOGON_PROOF_S +{ + uint8 cmd; + uint8 error; + uint8 M2[20]; + uint32 unk1; + uint32 unk2; + uint16 unk3; +} sAuthLogonProof_S; + +typedef struct AUTH_LOGON_PROOF_S_OLD +{ + uint8 cmd; + uint8 error; + uint8 M2[20]; + uint32 unk2; +} sAuthLogonProof_S_Old; + +typedef struct AUTH_RECONNECT_PROOF_C +{ + uint8 cmd; + uint8 R1[16]; + uint8 R2[20]; + uint8 R3[20]; + uint8 number_of_keys; +} sAuthReconnectProof_C; + +#pragma pack(pop) + + +typedef struct AuthHandler +{ + eAuthCmd cmd; + uint32 status; + size_t packetSize; + bool (AuthSession::*handler)(); +} AuthHandler; + +#define BYTE_SIZE 32 +#define REALMLIST_SKIP_PACKETS 5 + +const AuthHandler table[] = +{ + { AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, sizeof(AUTH_LOGON_CHALLENGE_C), &AuthSession::_HandleLogonChallenge }, + { AUTH_LOGON_PROOF, STATUS_CONNECTED, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::_HandleLogonProof }, + { AUTH_RECONNECT_CHALLENGE, STATUS_CONNECTED, sizeof(AUTH_LOGON_CHALLENGE_C), &AuthSession::_HandleReconnectChallenge }, + { AUTH_RECONNECT_PROOF, STATUS_CONNECTED, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::_HandleReconnectProof }, + { REALM_LIST, STATUS_AUTHED, REALMLIST_SKIP_PACKETS, &AuthSession::_HandleRealmList } +}; + +void AuthSession::AsyncReadHeader() +{ + auto self(shared_from_this()); + + _socket.async_read_some(boost::asio::buffer(_readBuffer, 1), [this, self](boost::system::error_code error, size_t transferedBytes) + { + if (!error && transferedBytes == 1) + { + for (const AuthHandler& entry : table) + { + if ((uint8)entry.cmd == _readBuffer[0] && (entry.status == STATUS_CONNECTED || (_isAuthenticated && entry.status == STATUS_AUTHED))) + { + // Handle dynamic size packet + if (_readBuffer[0] == AUTH_LOGON_CHALLENGE) + { + _socket.read_some(boost::asio::buffer(&_readBuffer[1], sizeof(uint8) + sizeof(uint16))); //error + size + + AsyncReadData(entry.handler, (uint16)&_readBuffer[2], sizeof(uint8) + sizeof(uint8) + sizeof(uint16)); // cmd + error + size + } + else + { + AsyncReadData(entry.handler, entry.packetSize, sizeof(uint8)); + } + break; + } + } + } + else + { + _socket.close(); + } + }); +} + +void AuthSession::AsyncReadData(bool (AuthSession::*handler)(), size_t dataSize, size_t bufferOffSet) +{ + auto self(shared_from_this()); + + _socket.async_read_some(boost::asio::buffer(&_readBuffer[bufferOffSet], dataSize), [handler, this, self](boost::system::error_code error, size_t transferedBytes) + { + if (!error && transferedBytes > 0) + { + if (!(*this.*handler)()) + { + _socket.close(); + return; + } + + AsyncReadHeader(); + } + else + { + _socket.close(); + } + }); +} + +void AuthSession::AsyncWrite(std::size_t length) +{ + boost::asio::async_write(_socket, boost::asio::buffer(_writeBuffer, length), [this](boost::system::error_code error, std::size_t /*length*/) + { + if (error) + { + _socket.close(); + } + }); +} + +bool AuthSession::_HandleLogonChallenge() +{ + sAuthLogonChallenge_C *challenge = (sAuthLogonChallenge_C*)&_readBuffer; + + //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size); + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I); + + ByteBuffer pkt; + + _login.assign((const char*)challenge->I, challenge->I_len); + _build = challenge->build; + _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); + _os = (const char*)challenge->os; + + if (_os.size() > 4) + return false; + + // Restore string order as its byte order is reversed + std::reverse(_os.begin(), _os.end()); + + pkt << uint8(AUTH_LOGON_CHALLENGE); + pkt << uint8(0x00); + + // Verify that this IP is not in the ip_banned table + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); + + std::string const& ipAddress = _socket.remote_endpoint().address().to_string(); + unsigned short port = _socket.remote_endpoint().port(); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED); + stmt->setString(0, ipAddress); + PreparedQueryResult result = LoginDatabase.Query(stmt); + if (result) + { + pkt << uint8(WOW_FAIL_BANNED); + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned ip tries to login!", ipAddress.c_str(), port); + } + else + { + // Get the account details from the account table + // No SQL injection (prepared statement) + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE); + stmt->setString(0, _login); + + PreparedQueryResult res2 = LoginDatabase.Query(stmt); + if (res2) + { + Field* fields = res2->Fetch(); + + // If the IP is 'locked', check that the player comes indeed from the correct IP address + bool locked = false; + if (fields[2].GetUInt8() == 1) // if ip is locked + { + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), fields[4].GetCString()); + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Player address is '%s'", ipAddress.c_str()); + + if (strcmp(fields[4].GetCString(), ipAddress.c_str()) != 0) + { + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP differs"); + pkt << uint8(WOW_FAIL_LOCKED_ENFORCED); + locked = true; + } + else + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP matches"); + } + else + { + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _login.c_str()); + std::string accountCountry = fields[3].GetString(); + if (accountCountry.empty() || accountCountry == "00") + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _login.c_str()); + else if (!accountCountry.empty()) + { + uint32 ip = inet_addr(ipAddress.c_str()); + EndianConvertReverse(ip); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY); + stmt->setUInt32(0, ip); + if (PreparedQueryResult sessionCountryQuery = LoginDatabase.Query(stmt)) + { + std::string loginCountry = (*sessionCountryQuery)[0].GetString(); + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _login.c_str(), + accountCountry.c_str(), loginCountry.c_str()); + + if (loginCountry != accountCountry) + { + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country differs."); + pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK); + locked = true; + } + else + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country matches"); + } + else + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] IP2NATION Table empty"); + } + } + + if (!locked) + { + //set expired bans to inactive + LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); + + // If the account is banned, reject the logon attempt + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED); + stmt->setUInt32(0, fields[1].GetUInt32()); + PreparedQueryResult banresult = LoginDatabase.Query(stmt); + if (banresult) + { + if ((*banresult)[0].GetUInt32() == (*banresult)[1].GetUInt32()) + { + pkt << uint8(WOW_FAIL_BANNED); + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", ipAddress.c_str(), + port, _login.c_str()); + } + else + { + pkt << uint8(WOW_FAIL_SUSPENDED); + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", + ipAddress.c_str(), port, _login.c_str()); + } + } + else + { + // Get the password from the account table, upper it, and make the SRP6 calculation + std::string rI = fields[0].GetString(); + + // Don't calculate (v, s) if there are already some in the database + std::string databaseV = fields[6].GetString(); + std::string databaseS = fields[7].GetString(); + + TC_LOG_DEBUG("network", "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str()); + + // multiply with 2 since bytes are stored as hexstring + if (databaseV.size() != BYTE_SIZE * 2 || databaseS.size() != BYTE_SIZE * 2) + SetVSFields(rI); + else + { + s.SetHexStr(databaseS.c_str()); + v.SetHexStr(databaseV.c_str()); + } + + b.SetRand(19 * 8); + BigNumber gmod = g.ModExp(b, N); + B = ((v * 3) + gmod) % N; + + ASSERT(gmod.GetNumBytes() <= 32); + + BigNumber unk3; + unk3.SetRand(16 * 8); + + // Fill the response packet with the result + if (AuthHelper::IsAcceptedClientBuild(_build)) + pkt << uint8(WOW_SUCCESS); + else + pkt << uint8(WOW_FAIL_VERSION_INVALID); + + // B may be calculated < 32B so we force minimal length to 32B + pkt.append(B.AsByteArray(32).get(), 32); // 32 bytes + pkt << uint8(1); + pkt.append(g.AsByteArray().get(), 1); + pkt << uint8(32); + pkt.append(N.AsByteArray(32).get(), 32); + pkt.append(s.AsByteArray().get(), s.GetNumBytes()); // 32 bytes + pkt.append(unk3.AsByteArray(16).get(), 16); + uint8 securityFlags = 0; + + // Check if token is used + _tokenKey = fields[8].GetString(); + if (!_tokenKey.empty()) + securityFlags = 4; + + pkt << uint8(securityFlags); // security flags (0x0...0x04) + + if (securityFlags & 0x01) // PIN input + { + pkt << uint32(0); + pkt << uint64(0) << uint64(0); // 16 bytes hash? + } + + if (securityFlags & 0x02) // Matrix input + { + pkt << uint8(0); + pkt << uint8(0); + pkt << uint8(0); + pkt << uint8(0); + pkt << uint64(0); + } + + if (securityFlags & 0x04) // Security token input + pkt << uint8(1); + + uint8 secLevel = fields[5].GetUInt8(); + _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; + + _localizationName.resize(4); + for (int i = 0; i < 4; ++i) + _localizationName[i] = challenge->country[4 - i - 1]; + + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", + ipAddress.c_str(), port, _login.c_str(), + challenge->country[3], challenge->country[2], challenge->country[1], challenge->country[0], + GetLocaleByName(_localizationName) + ); + } + } + } + else //no account + pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); + } + + std::memcpy(_writeBuffer, (char const*)pkt.contents(), pkt.size()); + + AsyncWrite(pkt.size()); + + return true; +} + +// Logon Proof command handler +bool AuthSession::_HandleLogonProof() +{ + + TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof"); + // Read the packet + sAuthLogonProof_C *logonProof = (sAuthLogonProof_C*)&_readBuffer; + + // If the client has no valid version + if (_expversion == NO_VALID_EXP_FLAG) + { + // Check if we have the appropriate patch on the disk + TC_LOG_DEBUG("network", "Client with invalid version, patching is not implemented"); + return false; + } + + // Continue the SRP6 calculation based on data received from the client + BigNumber A; + + A.SetBinary(logonProof->A, 32); + + // SRP safeguard: abort if A == 0 + if (A.isZero()) + { + return false; + } + + SHA1Hash sha; + sha.UpdateBigNumbers(&A, &B, NULL); + sha.Finalize(); + BigNumber u; + u.SetBinary(sha.GetDigest(), 20); + BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N); + + uint8 t[32]; + uint8 t1[16]; + uint8 vK[40]; + memcpy(t, S.AsByteArray(32).get(), 32); + + for (int i = 0; i < 16; ++i) + t1[i] = t[i * 2]; + + sha.Initialize(); + sha.UpdateData(t1, 16); + sha.Finalize(); + + for (int i = 0; i < 20; ++i) + vK[i * 2] = sha.GetDigest()[i]; + + for (int i = 0; i < 16; ++i) + t1[i] = t[i * 2 + 1]; + + sha.Initialize(); + sha.UpdateData(t1, 16); + sha.Finalize(); + + for (int i = 0; i < 20; ++i) + vK[i * 2 + 1] = sha.GetDigest()[i]; + + K.SetBinary(vK, 40); + + uint8 hash[20]; + + sha.Initialize(); + sha.UpdateBigNumbers(&N, NULL); + sha.Finalize(); + memcpy(hash, sha.GetDigest(), 20); + sha.Initialize(); + sha.UpdateBigNumbers(&g, NULL); + sha.Finalize(); + + for (int i = 0; i < 20; ++i) + hash[i] ^= sha.GetDigest()[i]; + + BigNumber t3; + t3.SetBinary(hash, 20); + + sha.Initialize(); + sha.UpdateData(_login); + sha.Finalize(); + uint8 t4[SHA_DIGEST_LENGTH]; + memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH); + + sha.Initialize(); + sha.UpdateBigNumbers(&t3, NULL); + sha.UpdateData(t4, SHA_DIGEST_LENGTH); + sha.UpdateBigNumbers(&s, &A, &B, &K, NULL); + sha.Finalize(); + BigNumber M; + M.SetBinary(sha.GetDigest(), 20); + + // Check if SRP6 results match (password is correct), else send an error + if (!memcmp(M.AsByteArray().get(), logonProof->M1, 20)) + { + TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().c_str(), GetRemotePort(), _login.c_str()); + + // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account + // No SQL injection (escaped user name) and IP address as received by socket + const char *K_hex = K.AsHexStr(); + + PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); + stmt->setString(0, K_hex); + stmt->setString(1, GetRemoteIpAddress().c_str()); + stmt->setUInt32(2, GetLocaleByName(_localizationName)); + stmt->setString(3, _os); + stmt->setString(4, _login); + LoginDatabase.DirectExecute(stmt); + + OPENSSL_free((void*)K_hex); + + // Finish SRP6 and send the final result to the client + sha.Initialize(); + sha.UpdateBigNumbers(&A, &M, &K, NULL); + sha.Finalize(); + + // Check auth token + if ((logonProof->securityFlags & 0x04) || !_tokenKey.empty()) + { + // TODO To be fixed + + /* + uint8 size; + socket().recv((char*)&size, 1); + char* token = new char[size + 1]; + token[size] = '\0'; + socket().recv(token, size); + unsigned int validToken = TOTP::GenerateToken(_tokenKey.c_str()); + unsigned int incomingToken = atoi(token); + delete[] token; + if (validToken != incomingToken) + { + char data[] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 }; + socket().send(data, sizeof(data)); + return false; + }*/ + } + + if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients + { + sAuthLogonProof_S proof; + memcpy(proof.M2, sha.GetDigest(), 20); + proof.cmd = AUTH_LOGON_PROOF; + proof.error = 0; + proof.unk1 = 0x00800000; // Accountflags. 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament) + proof.unk2 = 0x00; // SurveyId + proof.unk3 = 0x00; + + std::memcpy(_writeBuffer, (char *)&proof, sizeof(proof)); + AsyncWrite(sizeof(proof)); + } + else + { + sAuthLogonProof_S_Old proof; + memcpy(proof.M2, sha.GetDigest(), 20); + proof.cmd = AUTH_LOGON_PROOF; + proof.error = 0; + proof.unk2 = 0x00; + + std::memcpy(_writeBuffer, (char *)&proof, sizeof(proof)); + AsyncWrite(sizeof(proof)); + } + + _isAuthenticated = true; + } + else + { + char data[4] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 }; + + std::memcpy(_writeBuffer, data, sizeof(data)); + AsyncWrite(sizeof(data)); + + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", + GetRemoteIpAddress().c_str(), GetRemotePort(), _login.c_str()); + + uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0); + if (MaxWrongPassCount > 0) + { + //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP + PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS); + stmt->setString(0, _login); + LoginDatabase.Execute(stmt); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_FAILEDLOGINS); + stmt->setString(0, _login); + + if (PreparedQueryResult loginfail = LoginDatabase.Query(stmt)) + { + uint32 failed_logins = (*loginfail)[1].GetUInt32(); + + if (failed_logins >= MaxWrongPassCount) + { + uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600); + bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false); + + if (WrongPassBanType) + { + uint32 acc_id = (*loginfail)[0].GetUInt32(); + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED); + stmt->setUInt32(0, acc_id); + stmt->setUInt32(1, WrongPassBanTime); + LoginDatabase.Execute(stmt); + + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", + GetRemoteIpAddress().c_str(), GetRemotePort(), _login.c_str(), WrongPassBanTime, failed_logins); + } + else + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED); + stmt->setString(0, GetRemoteIpAddress()); + stmt->setUInt32(1, WrongPassBanTime); + LoginDatabase.Execute(stmt); + + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] IP got banned for '%u' seconds because account %s failed to authenticate '%u' times", + GetRemoteIpAddress().c_str(), GetRemotePort(), WrongPassBanTime, _login.c_str(), failed_logins); + } + } + } + } + } + + return true; +} + +bool AuthSession::_HandleReconnectChallenge() +{ + TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectChallenge"); + sAuthLogonChallenge_C *challenge = (sAuthLogonChallenge_C*)&_readBuffer; + + //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size); + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I); + + _login.assign((const char*)challenge->I, challenge->I_len); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SESSIONKEY); + stmt->setString(0, _login); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + // Stop if the account is not found + if (!result) + { + TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login and we cannot find his session key in the database.", + GetRemoteIpAddress().c_str(), GetRemotePort(), _login.c_str()); + return false; + } + + // Reinitialize build, expansion and the account securitylevel + _build = challenge->build; + _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); + _os = (const char*)challenge->os; + + if (_os.size() > 4) + return false; + + // Restore string order as its byte order is reversed + std::reverse(_os.begin(), _os.end()); + + Field* fields = result->Fetch(); + uint8 secLevel = fields[2].GetUInt8(); + _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; + + K.SetHexStr((*result)[0].GetCString()); + + // Sending response + ByteBuffer pkt; + pkt << uint8(AUTH_RECONNECT_CHALLENGE); + pkt << uint8(0x00); + _reconnectProof.SetRand(16 * 8); + pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random + pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros + + std::memcpy(_writeBuffer, (char const*)pkt.contents(), pkt.size()); + AsyncWrite(pkt.size()); + + return true; +} +bool AuthSession::_HandleReconnectProof() +{ + TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof"); + sAuthReconnectProof_C *reconnectProof = (sAuthReconnectProof_C*)&_readBuffer; + + if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) + return false; + + BigNumber t1; + t1.SetBinary(reconnectProof->R1, 16); + + SHA1Hash sha; + sha.Initialize(); + sha.UpdateData(_login); + sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, NULL); + sha.Finalize(); + + if (!memcmp(sha.GetDigest(), reconnectProof->R2, SHA_DIGEST_LENGTH)) + { + // Sending response + ByteBuffer pkt; + pkt << uint8(AUTH_RECONNECT_PROOF); + pkt << uint8(0x00); + pkt << uint16(0x00); // 2 bytes zeros + std::memcpy(_writeBuffer, (char const*)pkt.contents(), pkt.size()); + AsyncWrite(pkt.size()); + _isAuthenticated = true; + return true; + } + else + { + TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", GetRemoteIpAddress().c_str(), + GetRemotePort(), _login.c_str()); + return false; + } +} + +ACE_INET_Addr const& GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr) +{ + // Attempt to send best address for client + if (clientAddr.is_loopback()) + { + // Try guessing if realm is also connected locally + if (realm.LocalAddress.is_loopback() || realm.ExternalAddress.is_loopback()) + return clientAddr; + + // Assume that user connecting from the machine that authserver is located on + // has all realms available in his local network + return realm.LocalAddress; + } + + // Check if connecting client is in the same network + if (IsIPAddrInNetwork(realm.LocalAddress, clientAddr, realm.LocalSubnetMask)) + return realm.LocalAddress; + + // Return external IP + return realm.ExternalAddress; +} + + +bool AuthSession::_HandleRealmList() +{ + TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList"); + + // Get the user id (else close the connection) + // No SQL injection (prepared statement) + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME); + stmt->setString(0, _login); + PreparedQueryResult result = LoginDatabase.Query(stmt); + if (!result) + { + TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login but we cannot find him in the database.", GetRemoteIpAddress().c_str(), + GetRemotePort(), _login.c_str()); + return false; + } + + Field* fields = result->Fetch(); + uint32 id = fields[0].GetUInt32(); + + // Update realm list if need + sRealmList->UpdateIfNeed(); + + // Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm) + ByteBuffer pkt; + + size_t RealmListSize = 0; + for (RealmList::RealmMap::const_iterator i = sRealmList->begin(); i != sRealmList->end(); ++i) + { + const Realm &realm = i->second; + // don't work with realms which not compatible with the client + bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.gamebuild == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.gamebuild)); + + // No SQL injection. id of realm is controlled by the database. + uint32 flag = realm.flag; + RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm.gamebuild); + if (!okBuild) + { + if (!buildInfo) + continue; + + flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for + } + + if (!buildInfo) + flag &= ~REALM_FLAG_SPECIFYBUILD; + + std::string name = i->first; + if (_expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD) + { + std::ostringstream ss; + ss << name << " (" << buildInfo->MajorVersion << '.' << buildInfo->MinorVersion << '.' << buildInfo->BugfixVersion << ')'; + name = ss.str(); + } + + // We don't need the port number from which client connects with but the realm's port + ACE_INET_Addr clientAddr(realm.ExternalAddress.get_port_number(), GetRemoteIpAddress().c_str(), AF_INET); + + uint8 lock = (realm.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0; + + uint8 AmountOfCharacters = 0; + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_NUM_CHARS_ON_REALM); + stmt->setUInt32(0, realm.m_ID); + stmt->setUInt32(1, id); + result = LoginDatabase.Query(stmt); + if (result) + AmountOfCharacters = (*result)[0].GetUInt8(); + + pkt << realm.icon; // realm type + if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients + pkt << lock; // if 1, then realm locked + pkt << uint8(flag); // RealmFlags + pkt << name; + pkt << GetAddressString(GetAddressForClient(realm, clientAddr)); + pkt << realm.populationLevel; + pkt << AmountOfCharacters; + pkt << realm.timezone; // realm category + if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients + pkt << uint8(0x2C); // unk, may be realm number/id? + else + pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients + + if (_expversion & POST_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD) + { + pkt << uint8(buildInfo->MajorVersion); + pkt << uint8(buildInfo->MinorVersion); + pkt << uint8(buildInfo->BugfixVersion); + pkt << uint16(buildInfo->Build); + } + + ++RealmListSize; + } + + if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients + { + pkt << uint8(0x10); + pkt << uint8(0x00); + } + else // 1.12.1 and 1.12.2 clients + { + pkt << uint8(0x00); + pkt << uint8(0x02); + } + + // make a ByteBuffer which stores the RealmList's size + ByteBuffer RealmListSizeBuffer; + RealmListSizeBuffer << uint32(0); + if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients + RealmListSizeBuffer << uint16(RealmListSize); + else + RealmListSizeBuffer << uint32(RealmListSize); + + ByteBuffer hdr; + hdr << uint8(REALM_LIST); + hdr << uint16(pkt.size() + RealmListSizeBuffer.size()); + hdr.append(RealmListSizeBuffer); // append RealmList's size buffer + hdr.append(pkt); // append realms in the realmlist + + std::memcpy(_writeBuffer, (char const*)hdr.contents(), hdr.size()); + AsyncWrite(hdr.size()); + + return true; +} + +// Make the SRP6 calculation from hash in dB +void AuthSession::SetVSFields(const std::string& rI) +{ + s.SetRand(BYTE_SIZE * 8); + + BigNumber I; + I.SetHexStr(rI.c_str()); + + // In case of leading zeros in the rI hash, restore them + uint8 mDigest[SHA_DIGEST_LENGTH]; + memset(mDigest, 0, SHA_DIGEST_LENGTH); + if (I.GetNumBytes() <= SHA_DIGEST_LENGTH) + memcpy(mDigest, I.AsByteArray().get(), I.GetNumBytes()); + + std::reverse(mDigest, mDigest + SHA_DIGEST_LENGTH); + + SHA1Hash sha; + sha.UpdateData(s.AsByteArray().get(), s.GetNumBytes()); + sha.UpdateData(mDigest, SHA_DIGEST_LENGTH); + sha.Finalize(); + BigNumber x; + x.SetBinary(sha.GetDigest(), sha.GetLength()); + v = g.ModExp(x, N); + + // No SQL injection (username escaped) + char *v_hex, *s_hex; + v_hex = v.AsHexStr(); + s_hex = s.AsHexStr(); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS); + stmt->setString(0, v_hex); + stmt->setString(1, s_hex); + stmt->setString(2, _login); + LoginDatabase.Execute(stmt); + + OPENSSL_free(v_hex); + OPENSSL_free(s_hex); +}
\ No newline at end of file diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h new file mode 100644 index 00000000000..205300e806a --- /dev/null +++ b/src/server/authserver/Server/AuthSession.h @@ -0,0 +1,85 @@ +/* +* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* 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 2 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/>. +*/ + +#ifndef __AUTHSESSION_H__ +#define __AUTHSESSION_H__ + +#include <memory> +#include <boost/asio.hpp> + +#include "Common.h" +#include "BigNumber.h" + +using boost::asio::ip::tcp; + +const size_t bufferSize = 4096; + +#define BUFFER_SIZE 4096 + +class AuthSession : public std::enable_shared_from_this < AuthSession > +{ +public: + AuthSession(tcp::socket socket) : _socket(std::move(socket)) + { + N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); + g.SetDword(7); + } + + void Start() + { + AsyncReadHeader(); + } + + bool _HandleLogonChallenge(); + bool _HandleLogonProof(); + bool _HandleReconnectChallenge(); + bool _HandleReconnectProof(); + bool _HandleRealmList(); + + const std::string GetRemoteIpAddress() const { return _socket.remote_endpoint().address().to_string(); }; + unsigned short GetRemotePort() const { return _socket.remote_endpoint().port(); } + +private: + void AsyncReadHeader(); + void AsyncReadData(bool (AuthSession::*handler)(), size_t dataSize, size_t bufferOffset); + void AsyncWrite(size_t length); + + + void SetVSFields(const std::string& rI); + + BigNumber N, s, g, v; + BigNumber b, B; + BigNumber K; + BigNumber _reconnectProof; + + tcp::socket _socket; + char _readBuffer[BUFFER_SIZE]; + char _writeBuffer[BUFFER_SIZE]; + + bool _isAuthenticated; + std::string _tokenKey; + std::string _login; + std::string _localizationName; + std::string _os; + uint16 _build; + uint8 _expversion; + + AccountTypes _accountSecurityLevel; +}; + +#endif
\ No newline at end of file diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp deleted file mode 100644 index c7bb600024a..00000000000 --- a/src/server/authserver/Server/AuthSocket.cpp +++ /dev/null @@ -1,1207 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * 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 2 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/>. - */ - -#include <algorithm> -#include <openssl/md5.h> - -#include "Common.h" -#include "Database/DatabaseEnv.h" -#include "ByteBuffer.h" -#include "Configuration/Config.h" -#include "Log.h" -#include "RealmList.h" -#include "AuthSocket.h" -#include "AuthCodes.h" -#include "TOTP.h" -#include "SHA1.h" -#include "openssl/crypto.h" - -#define ChunkSize 2048 - -enum eAuthCmd -{ - AUTH_LOGON_CHALLENGE = 0x00, - AUTH_LOGON_PROOF = 0x01, - AUTH_RECONNECT_CHALLENGE = 0x02, - AUTH_RECONNECT_PROOF = 0x03, - REALM_LIST = 0x10, - XFER_INITIATE = 0x30, - XFER_DATA = 0x31, - XFER_ACCEPT = 0x32, - XFER_RESUME = 0x33, - XFER_CANCEL = 0x34 -}; - -enum eStatus -{ - STATUS_CONNECTED = 0, - STATUS_AUTHED -}; - -// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push, N), also any gcc version not support it at some paltform -#if defined(__GNUC__) -#pragma pack(1) -#else -#pragma pack(push, 1) -#endif - -typedef struct AUTH_LOGON_CHALLENGE_C -{ - uint8 cmd; - uint8 error; - uint16 size; - uint8 gamename[4]; - uint8 version1; - uint8 version2; - uint8 version3; - uint16 build; - uint8 platform[4]; - uint8 os[4]; - uint8 country[4]; - uint32 timezone_bias; - uint32 ip; - uint8 I_len; - uint8 I[1]; -} sAuthLogonChallenge_C; - -typedef struct AUTH_LOGON_PROOF_C -{ - uint8 cmd; - uint8 A[32]; - uint8 M1[20]; - uint8 crc_hash[20]; - uint8 number_of_keys; - uint8 securityFlags; // 0x00-0x04 -} sAuthLogonProof_C; - -typedef struct AUTH_LOGON_PROOF_S -{ - uint8 cmd; - uint8 error; - uint8 M2[20]; - uint32 unk1; - uint32 unk2; - uint16 unk3; -} sAuthLogonProof_S; - -typedef struct AUTH_LOGON_PROOF_S_OLD -{ - uint8 cmd; - uint8 error; - uint8 M2[20]; - uint32 unk2; -} sAuthLogonProof_S_Old; - -typedef struct AUTH_RECONNECT_PROOF_C -{ - uint8 cmd; - uint8 R1[16]; - uint8 R2[20]; - uint8 R3[20]; - uint8 number_of_keys; -} sAuthReconnectProof_C; - -typedef struct XFER_INIT -{ - uint8 cmd; // XFER_INITIATE - uint8 fileNameLen; // strlen(fileName); - uint8 fileName[5]; // fileName[fileNameLen] - uint64 file_size; // file size (bytes) - uint8 md5[MD5_DIGEST_LENGTH]; // MD5 -} XFER_INIT; - -typedef struct XFER_DATA -{ - uint8 opcode; - uint16 data_size; - uint8 data[ChunkSize]; -} XFER_DATA_STRUCT; - -typedef struct AuthHandler -{ - eAuthCmd cmd; - uint32 status; - bool (AuthSocket::*handler)(void); -} AuthHandler; - -// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform -#if defined(__GNUC__) -#pragma pack() -#else -#pragma pack(pop) -#endif - -// Launch a thread to transfer a patch to the client -class PatcherRunnable: public ACE_Based::Runnable -{ -public: - PatcherRunnable(class AuthSocket*); - void run(); - -private: - AuthSocket* mySocket; -}; - -typedef struct PATCH_INFO -{ - uint8 md5[MD5_DIGEST_LENGTH]; -} PATCH_INFO; - -// Caches MD5 hash of client patches present on the server -class Patcher -{ -public: - typedef std::map<std::string, PATCH_INFO*> Patches; - ~Patcher(); - Patcher(); - Patches::const_iterator begin() const { return _patches.begin(); } - Patches::const_iterator end() const { return _patches.end(); } - void LoadPatchMD5(char*); - bool GetHash(char * pat, uint8 mymd5[16]); - -private: - void LoadPatchesInfo(); - Patches _patches; -}; - -const AuthHandler table[] = -{ - { AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge }, - { AUTH_LOGON_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleLogonProof }, - { AUTH_RECONNECT_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleReconnectChallenge}, - { AUTH_RECONNECT_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleReconnectProof }, - { REALM_LIST, STATUS_AUTHED, &AuthSocket::_HandleRealmList }, - { XFER_ACCEPT, STATUS_CONNECTED, &AuthSocket::_HandleXferAccept }, - { XFER_RESUME, STATUS_CONNECTED, &AuthSocket::_HandleXferResume }, - { XFER_CANCEL, STATUS_CONNECTED, &AuthSocket::_HandleXferCancel } -}; - -#define AUTH_TOTAL_COMMANDS 8 - -// Holds the MD5 hash of client patches present on the server -Patcher PatchesCache; - -// Constructor - set the N and g values for SRP6 -AuthSocket::AuthSocket(RealmSocket& socket) : - pPatch(NULL), socket_(socket), _authed(false), _build(0), - _expversion(0), _accountSecurityLevel(SEC_PLAYER) -{ - N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); - g.SetDword(7); -} - -// Close patch file descriptor before leaving -AuthSocket::~AuthSocket(void) { } - -// Accept the connection -void AuthSocket::OnAccept(void) -{ - TC_LOG_DEBUG("server.authserver", "'%s:%d' Accepting connection", socket().getRemoteAddress().c_str(), socket().getRemotePort()); -} - -void AuthSocket::OnClose(void) -{ - TC_LOG_DEBUG("server.authserver", "AuthSocket::OnClose"); -} - -// Read the packet from the client -void AuthSocket::OnRead() -{ - #define MAX_AUTH_LOGON_CHALLENGES_IN_A_ROW 3 - uint32 challengesInARow = 0; - uint8 _cmd; - while (1) - { - if (!socket().recv_soft((char *)&_cmd, 1)) - return; - - if (_cmd == AUTH_LOGON_CHALLENGE) - { - ++challengesInARow; - if (challengesInARow == MAX_AUTH_LOGON_CHALLENGES_IN_A_ROW) - { - TC_LOG_WARN("server.authserver", "Got %u AUTH_LOGON_CHALLENGE in a row from '%s', possible ongoing DoS", challengesInARow, socket().getRemoteAddress().c_str()); - socket().shutdown(); - return; - } - } - - size_t i; - - // Circle through known commands and call the correct command handler - for (i = 0; i < AUTH_TOTAL_COMMANDS; ++i) - { - if ((uint8)table[i].cmd == _cmd && (table[i].status == STATUS_CONNECTED || (_authed && table[i].status == STATUS_AUTHED))) - { - TC_LOG_DEBUG("server.authserver", "Got data for cmd %u recv length %u", (uint32)_cmd, (uint32)socket().recv_len()); - - if (!(*this.*table[i].handler)()) - { - TC_LOG_DEBUG("server.authserver", "Command handler failed for cmd %u recv length %u", (uint32)_cmd, (uint32)socket().recv_len()); - return; - } - break; - } - } - - // Report unknown packets in the error log - if (i == AUTH_TOTAL_COMMANDS) - { - TC_LOG_ERROR("server.authserver", "Got unknown packet from '%s'", socket().getRemoteAddress().c_str()); - socket().shutdown(); - return; - } - } -} - -// Make the SRP6 calculation from hash in dB -void AuthSocket::_SetVSFields(const std::string& rI) -{ - s.SetRand(s_BYTE_SIZE * 8); - - BigNumber I; - I.SetHexStr(rI.c_str()); - - // In case of leading zeros in the rI hash, restore them - uint8 mDigest[SHA_DIGEST_LENGTH]; - memset(mDigest, 0, SHA_DIGEST_LENGTH); - if (I.GetNumBytes() <= SHA_DIGEST_LENGTH) - memcpy(mDigest, I.AsByteArray().get(), I.GetNumBytes()); - - std::reverse(mDigest, mDigest + SHA_DIGEST_LENGTH); - - SHA1Hash sha; - sha.UpdateData(s.AsByteArray().get(), s.GetNumBytes()); - sha.UpdateData(mDigest, SHA_DIGEST_LENGTH); - sha.Finalize(); - BigNumber x; - x.SetBinary(sha.GetDigest(), sha.GetLength()); - v = g.ModExp(x, N); - - // No SQL injection (username escaped) - char *v_hex, *s_hex; - v_hex = v.AsHexStr(); - s_hex = s.AsHexStr(); - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS); - stmt->setString(0, v_hex); - stmt->setString(1, s_hex); - stmt->setString(2, _login); - LoginDatabase.Execute(stmt); - - OPENSSL_free(v_hex); - OPENSSL_free(s_hex); -} - -// Logon Challenge command handler -bool AuthSocket::_HandleLogonChallenge() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonChallenge"); - if (socket().recv_len() < sizeof(sAuthLogonChallenge_C)) - return false; - - // Read the first 4 bytes (header) to get the length of the remaining of the packet - std::vector<uint8> buf; - buf.resize(4); - - socket().recv((char *)&buf[0], 4); - - EndianConvertPtr<uint16>(&buf[0]); - - uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got header, body is %#04x bytes", remaining); - - if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (socket().recv_len() < remaining)) - return false; - - //No big fear of memory outage (size is int16, i.e. < 65536) - buf.resize(remaining + buf.size() + 1); - buf[buf.size() - 1] = 0; - sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; - - // Read the remaining of the packet - socket().recv((char *)&buf[4], remaining); - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", ch->size); - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I); - - // BigEndian code, nop in little endian case - // size already converted - EndianConvertPtr<uint32>(&ch->gamename[0]); - EndianConvert(ch->build); - EndianConvertPtr<uint32>(&ch->platform[0]); - EndianConvertPtr<uint32>(&ch->os[0]); - EndianConvertPtr<uint32>(&ch->country[0]); - EndianConvert(ch->timezone_bias); - EndianConvert(ch->ip); - - ByteBuffer pkt; - - _login = (const char*)ch->I; - _build = ch->build; - _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); - _os = (const char*)ch->os; - - if (_os.size() > 4) - return false; - - // Restore string order as its byte order is reversed - std::reverse(_os.begin(), _os.end()); - - pkt << uint8(AUTH_LOGON_CHALLENGE); - pkt << uint8(0x00); - - // Verify that this IP is not in the ip_banned table - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); - - std::string const& ip_address = socket().getRemoteAddress(); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED); - stmt->setString(0, ip_address); - PreparedQueryResult result = LoginDatabase.Query(stmt); - if (result) - { - pkt << uint8(WOW_FAIL_BANNED); - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned ip tries to login!", socket().getRemoteAddress().c_str(), socket().getRemotePort()); - } - else - { - // Get the account details from the account table - // No SQL injection (prepared statement) - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE); - stmt->setString(0, _login); - - PreparedQueryResult res2 = LoginDatabase.Query(stmt); - if (res2) - { - Field* fields = res2->Fetch(); - - // If the IP is 'locked', check that the player comes indeed from the correct IP address - bool locked = false; - if (fields[2].GetUInt8() == 1) // if ip is locked - { - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), fields[4].GetCString()); - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Player address is '%s'", ip_address.c_str()); - - if (strcmp(fields[4].GetCString(), ip_address.c_str()) != 0) - { - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP differs"); - pkt << uint8(WOW_FAIL_LOCKED_ENFORCED); - locked = true; - } - else - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP matches"); - } - else - { - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _login.c_str()); - std::string accountCountry = fields[3].GetString(); - if (accountCountry.empty() || accountCountry == "00") - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _login.c_str()); - else if (!accountCountry.empty()) - { - uint32 ip = inet_addr(ip_address.c_str()); - EndianConvertReverse(ip); - - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY); - stmt->setUInt32(0, ip); - if (PreparedQueryResult sessionCountryQuery = LoginDatabase.Query(stmt)) - { - std::string loginCountry = (*sessionCountryQuery)[0].GetString(); - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _login.c_str(), accountCountry.c_str(), loginCountry.c_str()); - if (loginCountry != accountCountry) - { - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country differs."); - pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK); - locked = true; - } - else - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country matches"); - } - else - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] IP2NATION Table empty"); - } - } - - if (!locked) - { - //set expired bans to inactive - LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); - - // If the account is banned, reject the logon attempt - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED); - stmt->setUInt32(0, fields[1].GetUInt32()); - PreparedQueryResult banresult = LoginDatabase.Query(stmt); - if (banresult) - { - if ((*banresult)[0].GetUInt32() == (*banresult)[1].GetUInt32()) - { - pkt << uint8(WOW_FAIL_BANNED); - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str ()); - } - else - { - pkt << uint8(WOW_FAIL_SUSPENDED); - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str ()); - } - } - else - { - // Get the password from the account table, upper it, and make the SRP6 calculation - std::string rI = fields[0].GetString(); - - // Don't calculate (v, s) if there are already some in the database - std::string databaseV = fields[6].GetString(); - std::string databaseS = fields[7].GetString(); - - TC_LOG_DEBUG("network", "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str()); - - // multiply with 2 since bytes are stored as hexstring - if (databaseV.size() != s_BYTE_SIZE * 2 || databaseS.size() != s_BYTE_SIZE * 2) - _SetVSFields(rI); - else - { - s.SetHexStr(databaseS.c_str()); - v.SetHexStr(databaseV.c_str()); - } - - b.SetRand(19 * 8); - BigNumber gmod = g.ModExp(b, N); - B = ((v * 3) + gmod) % N; - - ASSERT(gmod.GetNumBytes() <= 32); - - BigNumber unk3; - unk3.SetRand(16 * 8); - - // Fill the response packet with the result - if (AuthHelper::IsAcceptedClientBuild(_build)) - pkt << uint8(WOW_SUCCESS); - else - pkt << uint8(WOW_FAIL_VERSION_INVALID); - - // B may be calculated < 32B so we force minimal length to 32B - pkt.append(B.AsByteArray(32).get(), 32); // 32 bytes - pkt << uint8(1); - pkt.append(g.AsByteArray().get(), 1); - pkt << uint8(32); - pkt.append(N.AsByteArray(32).get(), 32); - pkt.append(s.AsByteArray().get(), s.GetNumBytes()); // 32 bytes - pkt.append(unk3.AsByteArray(16).get(), 16); - uint8 securityFlags = 0; - - // Check if token is used - _tokenKey = fields[8].GetString(); - if (!_tokenKey.empty()) - securityFlags = 4; - - pkt << uint8(securityFlags); // security flags (0x0...0x04) - - if (securityFlags & 0x01) // PIN input - { - pkt << uint32(0); - pkt << uint64(0) << uint64(0); // 16 bytes hash? - } - - if (securityFlags & 0x02) // Matrix input - { - pkt << uint8(0); - pkt << uint8(0); - pkt << uint8(0); - pkt << uint8(0); - pkt << uint64(0); - } - - if (securityFlags & 0x04) // Security token input - pkt << uint8(1); - - uint8 secLevel = fields[5].GetUInt8(); - _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; - - _localizationName.resize(4); - for (int i = 0; i < 4; ++i) - _localizationName[i] = ch->country[4-i-1]; - - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", socket().getRemoteAddress().c_str(), socket().getRemotePort(), - _login.c_str (), ch->country[3], ch->country[2], ch->country[1], ch->country[0], GetLocaleByName(_localizationName) - ); - } - } - } - else //no account - pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); - } - - socket().send((char const*)pkt.contents(), pkt.size()); - return true; -} - -// Logon Proof command handler -bool AuthSocket::_HandleLogonProof() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof"); - // Read the packet - sAuthLogonProof_C lp; - - if (!socket().recv((char *)&lp, sizeof(sAuthLogonProof_C))) - return false; - - // If the client has no valid version - if (_expversion == NO_VALID_EXP_FLAG) - { - // Check if we have the appropriate patch on the disk - TC_LOG_DEBUG("network", "Client with invalid version, patching is not implemented"); - socket().shutdown(); - return true; - } - - // Continue the SRP6 calculation based on data received from the client - BigNumber A; - - A.SetBinary(lp.A, 32); - - // SRP safeguard: abort if A == 0 - if (A.isZero()) - { - socket().shutdown(); - return true; - } - - SHA1Hash sha; - sha.UpdateBigNumbers(&A, &B, NULL); - sha.Finalize(); - BigNumber u; - u.SetBinary(sha.GetDigest(), 20); - BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N); - - uint8 t[32]; - uint8 t1[16]; - uint8 vK[40]; - memcpy(t, S.AsByteArray(32).get(), 32); - - for (int i = 0; i < 16; ++i) - t1[i] = t[i * 2]; - - sha.Initialize(); - sha.UpdateData(t1, 16); - sha.Finalize(); - - for (int i = 0; i < 20; ++i) - vK[i * 2] = sha.GetDigest()[i]; - - for (int i = 0; i < 16; ++i) - t1[i] = t[i * 2 + 1]; - - sha.Initialize(); - sha.UpdateData(t1, 16); - sha.Finalize(); - - for (int i = 0; i < 20; ++i) - vK[i * 2 + 1] = sha.GetDigest()[i]; - - K.SetBinary(vK, 40); - - uint8 hash[20]; - - sha.Initialize(); - sha.UpdateBigNumbers(&N, NULL); - sha.Finalize(); - memcpy(hash, sha.GetDigest(), 20); - sha.Initialize(); - sha.UpdateBigNumbers(&g, NULL); - sha.Finalize(); - - for (int i = 0; i < 20; ++i) - hash[i] ^= sha.GetDigest()[i]; - - BigNumber t3; - t3.SetBinary(hash, 20); - - sha.Initialize(); - sha.UpdateData(_login); - sha.Finalize(); - uint8 t4[SHA_DIGEST_LENGTH]; - memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH); - - sha.Initialize(); - sha.UpdateBigNumbers(&t3, NULL); - sha.UpdateData(t4, SHA_DIGEST_LENGTH); - sha.UpdateBigNumbers(&s, &A, &B, &K, NULL); - sha.Finalize(); - BigNumber M; - M.SetBinary(sha.GetDigest(), 20); - - // Check if SRP6 results match (password is correct), else send an error - if (!memcmp(M.AsByteArray().get(), lp.M1, 20)) - { - TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str()); - - // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account - // No SQL injection (escaped user name) and IP address as received by socket - const char *K_hex = K.AsHexStr(); - - PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); - stmt->setString(0, K_hex); - stmt->setString(1, socket().getRemoteAddress().c_str()); - stmt->setUInt32(2, GetLocaleByName(_localizationName)); - stmt->setString(3, _os); - stmt->setString(4, _login); - LoginDatabase.DirectExecute(stmt); - - OPENSSL_free((void*)K_hex); - - // Finish SRP6 and send the final result to the client - sha.Initialize(); - sha.UpdateBigNumbers(&A, &M, &K, NULL); - sha.Finalize(); - - // Check auth token - if ((lp.securityFlags & 0x04) || !_tokenKey.empty()) - { - uint8 size; - socket().recv((char*)&size, 1); - char* token = new char[size + 1]; - token[size] = '\0'; - socket().recv(token, size); - unsigned int validToken = TOTP::GenerateToken(_tokenKey.c_str()); - unsigned int incomingToken = atoi(token); - delete[] token; - if (validToken != incomingToken) - { - char data[] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 }; - socket().send(data, sizeof(data)); - return false; - } - } - - if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients - { - sAuthLogonProof_S proof; - memcpy(proof.M2, sha.GetDigest(), 20); - proof.cmd = AUTH_LOGON_PROOF; - proof.error = 0; - proof.unk1 = 0x00800000; // Accountflags. 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament) - proof.unk2 = 0x00; // SurveyId - proof.unk3 = 0x00; - socket().send((char *)&proof, sizeof(proof)); - } - else - { - sAuthLogonProof_S_Old proof; - memcpy(proof.M2, sha.GetDigest(), 20); - proof.cmd = AUTH_LOGON_PROOF; - proof.error = 0; - proof.unk2 = 0x00; - socket().send((char *)&proof, sizeof(proof)); - } - - _authed = true; - } - else - { - char data[4] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 }; - socket().send(data, sizeof(data)); - - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str ()); - - uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0); - if (MaxWrongPassCount > 0) - { - //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP - PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS); - stmt->setString(0, _login); - LoginDatabase.Execute(stmt); - - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_FAILEDLOGINS); - stmt->setString(0, _login); - - if (PreparedQueryResult loginfail = LoginDatabase.Query(stmt)) - { - uint32 failed_logins = (*loginfail)[1].GetUInt32(); - - if (failed_logins >= MaxWrongPassCount) - { - uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600); - bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false); - - if (WrongPassBanType) - { - uint32 acc_id = (*loginfail)[0].GetUInt32(); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED); - stmt->setUInt32(0, acc_id); - stmt->setUInt32(1, WrongPassBanTime); - LoginDatabase.Execute(stmt); - - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", - socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str(), WrongPassBanTime, failed_logins); - } - else - { - stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED); - stmt->setString(0, socket().getRemoteAddress()); - stmt->setUInt32(1, WrongPassBanTime); - LoginDatabase.Execute(stmt); - - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times", - socket().getRemoteAddress().c_str(), socket().getRemotePort(), socket().getRemoteAddress().c_str(), WrongPassBanTime, _login.c_str(), failed_logins); - } - } - } - } - } - - return true; -} - -// Reconnect Challenge command handler -bool AuthSocket::_HandleReconnectChallenge() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectChallenge"); - if (socket().recv_len() < sizeof(sAuthLogonChallenge_C)) - return false; - - // Read the first 4 bytes (header) to get the length of the remaining of the packet - std::vector<uint8> buf; - buf.resize(4); - - socket().recv((char *)&buf[0], 4); - - EndianConvertPtr<uint16>(&buf[0]); - - uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; - TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] got header, body is %#04x bytes", remaining); - - if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (socket().recv_len() < remaining)) - return false; - - // No big fear of memory outage (size is int16, i.e. < 65536) - buf.resize(remaining + buf.size() + 1); - buf[buf.size() - 1] = 0; - sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; - - // Read the remaining of the packet - socket().recv((char *)&buf[4], remaining); - TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] got full packet, %#04x bytes", ch->size); - TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] name(%d): '%s'", ch->I_len, ch->I); - - _login = (const char*)ch->I; - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SESSIONKEY); - stmt->setString(0, _login); - PreparedQueryResult result = LoginDatabase.Query(stmt); - - // Stop if the account is not found - if (!result) - { - TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login and we cannot find his session key in the database.", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str()); - socket().shutdown(); - return false; - } - - // Reinitialize build, expansion and the account securitylevel - _build = ch->build; - _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); - _os = (const char*)ch->os; - - if (_os.size() > 4) - return false; - - // Restore string order as its byte order is reversed - std::reverse(_os.begin(), _os.end()); - - Field* fields = result->Fetch(); - uint8 secLevel = fields[2].GetUInt8(); - _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; - - K.SetHexStr ((*result)[0].GetCString()); - - // Sending response - ByteBuffer pkt; - pkt << uint8(AUTH_RECONNECT_CHALLENGE); - pkt << uint8(0x00); - _reconnectProof.SetRand(16 * 8); - pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random - pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros - socket().send((char const*)pkt.contents(), pkt.size()); - return true; -} - -// Reconnect Proof command handler -bool AuthSocket::_HandleReconnectProof() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof"); - // Read the packet - sAuthReconnectProof_C lp; - if (!socket().recv((char *)&lp, sizeof(sAuthReconnectProof_C))) - return false; - - if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) - return false; - - BigNumber t1; - t1.SetBinary(lp.R1, 16); - - SHA1Hash sha; - sha.Initialize(); - sha.UpdateData(_login); - sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, NULL); - sha.Finalize(); - - if (!memcmp(sha.GetDigest(), lp.R2, SHA_DIGEST_LENGTH)) - { - // Sending response - ByteBuffer pkt; - pkt << uint8(AUTH_RECONNECT_PROOF); - pkt << uint8(0x00); - pkt << uint16(0x00); // 2 bytes zeros - socket().send((char const*)pkt.contents(), pkt.size()); - _authed = true; - return true; - } - else - { - TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str()); - socket().shutdown(); - return false; - } -} - -ACE_INET_Addr const& AuthSocket::GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr) -{ - // Attempt to send best address for client - if (clientAddr.is_loopback()) - { - // Try guessing if realm is also connected locally - if (realm.LocalAddress.is_loopback() || realm.ExternalAddress.is_loopback()) - return clientAddr; - - // Assume that user connecting from the machine that authserver is located on - // has all realms available in his local network - return realm.LocalAddress; - } - - // Check if connecting client is in the same network - if (IsIPAddrInNetwork(realm.LocalAddress, clientAddr, realm.LocalSubnetMask)) - return realm.LocalAddress; - - // Return external IP - return realm.ExternalAddress; -} - -// Realm List command handler -bool AuthSocket::_HandleRealmList() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList"); - if (socket().recv_len() < 5) - return false; - - socket().recv_skip(5); - - // Get the user id (else close the connection) - // No SQL injection (prepared statement) - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME); - stmt->setString(0, _login); - PreparedQueryResult result = LoginDatabase.Query(stmt); - if (!result) - { - TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login but we cannot find him in the database.", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str()); - socket().shutdown(); - return false; - } - - Field* fields = result->Fetch(); - uint32 id = fields[0].GetUInt32(); - - // Update realm list if need - sRealmList->UpdateIfNeed(); - - ACE_INET_Addr clientAddr; - socket().peer().get_remote_addr(clientAddr); - - // Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm) - ByteBuffer pkt; - - size_t RealmListSize = 0; - for (RealmList::RealmMap::const_iterator i = sRealmList->begin(); i != sRealmList->end(); ++i) - { - const Realm &realm = i->second; - // don't work with realms which not compatible with the client - bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.gamebuild == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.gamebuild)); - - // No SQL injection. id of realm is controlled by the database. - uint32 flag = realm.flag; - RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm.gamebuild); - if (!okBuild) - { - if (!buildInfo) - continue; - - flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for - } - - if (!buildInfo) - flag &= ~REALM_FLAG_SPECIFYBUILD; - - std::string name = i->first; - if (_expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD) - { - std::ostringstream ss; - ss << name << " (" << buildInfo->MajorVersion << '.' << buildInfo->MinorVersion << '.' << buildInfo->BugfixVersion << ')'; - name = ss.str(); - } - - // We don't need the port number from which client connects with but the realm's port - clientAddr.set_port_number(realm.ExternalAddress.get_port_number()); - - uint8 lock = (realm.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0; - - uint8 AmountOfCharacters = 0; - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_NUM_CHARS_ON_REALM); - stmt->setUInt32(0, realm.m_ID); - stmt->setUInt32(1, id); - result = LoginDatabase.Query(stmt); - if (result) - AmountOfCharacters = (*result)[0].GetUInt8(); - - pkt << realm.icon; // realm type - if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients - pkt << lock; // if 1, then realm locked - pkt << uint8(flag); // RealmFlags - pkt << name; - pkt << GetAddressString(GetAddressForClient(realm, clientAddr)); - pkt << realm.populationLevel; - pkt << AmountOfCharacters; - pkt << realm.timezone; // realm category - if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients - pkt << uint8(0x2C); // unk, may be realm number/id? - else - pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients - - if (_expversion & POST_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD) - { - pkt << uint8(buildInfo->MajorVersion); - pkt << uint8(buildInfo->MinorVersion); - pkt << uint8(buildInfo->BugfixVersion); - pkt << uint16(buildInfo->Build); - } - - ++RealmListSize; - } - - if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients - { - pkt << uint8(0x10); - pkt << uint8(0x00); - } - else // 1.12.1 and 1.12.2 clients - { - pkt << uint8(0x00); - pkt << uint8(0x02); - } - - // make a ByteBuffer which stores the RealmList's size - ByteBuffer RealmListSizeBuffer; - RealmListSizeBuffer << uint32(0); - if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients - RealmListSizeBuffer << uint16(RealmListSize); - else - RealmListSizeBuffer << uint32(RealmListSize); - - ByteBuffer hdr; - hdr << uint8(REALM_LIST); - hdr << uint16(pkt.size() + RealmListSizeBuffer.size()); - hdr.append(RealmListSizeBuffer); // append RealmList's size buffer - hdr.append(pkt); // append realms in the realmlist - - socket().send((char const*)hdr.contents(), hdr.size()); - - return true; -} - -// Resume patch transfer -bool AuthSocket::_HandleXferResume() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleXferResume"); - // Check packet length and patch existence - if (socket().recv_len() < 9 || !pPatch) // FIXME: pPatch is never used - { - TC_LOG_ERROR("server.authserver", "Error while resuming patch transfer (wrong packet)"); - return false; - } - - // Launch a PatcherRunnable thread starting at given patch file offset - uint64 start; - socket().recv_skip(1); - socket().recv((char*)&start, sizeof(start)); - fseek(pPatch, long(start), 0); - - ACE_Based::Thread u(new PatcherRunnable(this)); - return true; -} - -// Cancel patch transfer -bool AuthSocket::_HandleXferCancel() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleXferCancel"); - - // Close and delete the socket - socket().recv_skip(1); //clear input buffer - socket().shutdown(); - - return true; -} - -// Accept patch transfer -bool AuthSocket::_HandleXferAccept() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleXferAccept"); - - // Check packet length and patch existence - if (!pPatch) - { - TC_LOG_ERROR("server.authserver", "Error while accepting patch transfer (wrong packet)"); - return false; - } - - // Launch a PatcherRunnable thread, starting at the beginning of the patch file - socket().recv_skip(1); // clear input buffer - fseek(pPatch, 0, 0); - - ACE_Based::Thread u(new PatcherRunnable(this)); - return true; -} - -PatcherRunnable::PatcherRunnable(class AuthSocket* as) -{ - mySocket = as; -} - -// Send content of patch file to the client -void PatcherRunnable::run() { } - -// Preload MD5 hashes of existing patch files on server -#ifndef _WIN32 -#include <dirent.h> -#include <errno.h> -void Patcher::LoadPatchesInfo() -{ - DIR *dirp; - struct dirent *dp; - dirp = opendir("./patches/"); - - if (!dirp) - return; - - while (dirp) - { - errno = 0; - if ((dp = readdir(dirp)) != NULL) - { - int l = strlen(dp->d_name); - - if (l < 8) - continue; - - if (!memcmp(&dp->d_name[l - 4], ".mpq", 4)) - LoadPatchMD5(dp->d_name); - } - else - { - if (errno != 0) - { - closedir(dirp); - return; - } - break; - } - } - - if (dirp) - closedir(dirp); -} -#else -void Patcher::LoadPatchesInfo() -{ - WIN32_FIND_DATA fil; - HANDLE hFil = FindFirstFile("./patches/*.mpq", &fil); - if (hFil == INVALID_HANDLE_VALUE) - return; // no patches were found - - do - LoadPatchMD5(fil.cFileName); - while (FindNextFile(hFil, &fil)); -} -#endif - -// Calculate and store MD5 hash for a given patch file -void Patcher::LoadPatchMD5(char *szFileName) -{ - // Try to open the patch file - std::string path = "./patches/"; - path += szFileName; - FILE* pPatch = fopen(path.c_str(), "rb"); - TC_LOG_DEBUG("network", "Loading patch info from %s\n", path.c_str()); - - if (!pPatch) - { - TC_LOG_ERROR("server.authserver", "Error loading patch %s\n", path.c_str()); - return; - } - - // Calculate the MD5 hash - MD5_CTX ctx; - MD5_Init(&ctx); - uint8* buf = new uint8[512 * 1024]; - - while (!feof(pPatch)) - { - size_t read = fread(buf, 1, 512 * 1024, pPatch); - MD5_Update(&ctx, buf, read); - } - - delete [] buf; - fclose(pPatch); - - // Store the result in the internal patch hash map - _patches[path] = new PATCH_INFO; - MD5_Final((uint8 *)&_patches[path]->md5, &ctx); -} - -// Get cached MD5 hash for a given patch file -bool Patcher::GetHash(char * pat, uint8 mymd5[16]) -{ - for (Patches::iterator i = _patches.begin(); i != _patches.end(); ++i) - if (!stricmp(pat, i->first.c_str())) - { - memcpy(mymd5, i->second->md5, 16); - return true; - } - - return false; -} - -// Launch the patch hashing mechanism on object creation -Patcher::Patcher() -{ - LoadPatchesInfo(); -} - -// Empty and delete the patch map on termination -Patcher::~Patcher() -{ - for (Patches::iterator i = _patches.begin(); i != _patches.end(); ++i) - delete i->second; -} diff --git a/src/server/authserver/Server/AuthSocket.h b/src/server/authserver/Server/AuthSocket.h deleted file mode 100644 index 5e04d459ba1..00000000000 --- a/src/server/authserver/Server/AuthSocket.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * 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 2 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/>. - */ - -#ifndef _AUTHSOCKET_H -#define _AUTHSOCKET_H - -#include "Common.h" -#include "BigNumber.h" -#include "RealmSocket.h" - -class ACE_INET_Addr; -struct Realm; - -// Handle login commands -class AuthSocket: public RealmSocket::Session -{ -public: - const static int s_BYTE_SIZE = 32; - - AuthSocket(RealmSocket& socket); - virtual ~AuthSocket(void); - - virtual void OnRead(void); - virtual void OnAccept(void); - virtual void OnClose(void); - - static ACE_INET_Addr const& GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr); - - bool _HandleLogonChallenge(); - bool _HandleLogonProof(); - bool _HandleReconnectChallenge(); - bool _HandleReconnectProof(); - bool _HandleRealmList(); - - //data transfer handle for patch - bool _HandleXferResume(); - bool _HandleXferCancel(); - bool _HandleXferAccept(); - - void _SetVSFields(const std::string& rI); - - FILE* pPatch; - ACE_Thread_Mutex patcherLock; - -private: - RealmSocket& socket_; - RealmSocket& socket(void) { return socket_; } - - BigNumber N, s, g, v; - BigNumber b, B; - BigNumber K; - BigNumber _reconnectProof; - - bool _authed; - - std::string _login; - std::string _tokenKey; - - // Since GetLocaleByName() is _NOT_ bijective, we have to store the locale as a string. Otherwise we can't differ - // between enUS and enGB, which is important for the patch system - std::string _localizationName; - std::string _os; - uint16 _build; - uint8 _expversion; - AccountTypes _accountSecurityLevel; -}; - -#endif diff --git a/src/server/authserver/Server/RealmAcceptor.h b/src/server/authserver/Server/RealmAcceptor.h deleted file mode 100644 index e89135c4896..00000000000 --- a/src/server/authserver/Server/RealmAcceptor.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/> - * - * 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 2 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/>. - */ - -#ifndef __REALMACCEPTOR_H__ -#define __REALMACCEPTOR_H__ - -#include <ace/Acceptor.h> -#include <ace/SOCK_Acceptor.h> - -#include "RealmSocket.h" -#include "AuthSocket.h" - -class RealmAcceptor : public ACE_Acceptor<RealmSocket, ACE_SOCK_Acceptor> -{ -public: - RealmAcceptor(void) { } - virtual ~RealmAcceptor(void) - { - if (reactor()) - reactor()->cancel_timer(this, 1); - } - -protected: - virtual int make_svc_handler(RealmSocket* &sh) - { - if (sh == 0) - ACE_NEW_RETURN(sh, RealmSocket, -1); - - sh->reactor(reactor()); - sh->set_session(new AuthSocket(*sh)); - return 0; - } - - virtual int handle_timeout(const ACE_Time_Value& /*current_time*/, const void* /*act = 0*/) - { - TC_LOG_DEBUG("server.authserver", "Resuming acceptor"); - reactor()->cancel_timer(this, 1); - return reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK); - } - - virtual int handle_accept_error(void) - { -#if defined(ENFILE) && defined(EMFILE) - if (errno == ENFILE || errno == EMFILE) - { - TC_LOG_ERROR("server.authserver", "Out of file descriptors, suspending incoming connections for 10 seconds"); - reactor()->remove_handler(this, ACE_Event_Handler::ACCEPT_MASK | ACE_Event_Handler::DONT_CALL); - reactor()->schedule_timer(this, NULL, ACE_Time_Value(10)); - } -#endif - return 0; - } -}; - -#endif diff --git a/src/server/authserver/Server/RealmSocket.cpp b/src/server/authserver/Server/RealmSocket.cpp deleted file mode 100644 index 1013639cc50..00000000000 --- a/src/server/authserver/Server/RealmSocket.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/> - * - * 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 2 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/>. - */ - -#include <ace/OS_NS_string.h> -#include <ace/INET_Addr.h> -#include <ace/SString.h> - -#include "RealmSocket.h" -#include "Log.h" - -RealmSocket::Session::Session(void) { } - -RealmSocket::Session::~Session(void) { } - -RealmSocket::RealmSocket(void) : - input_buffer_(4096), session_(NULL), - _remoteAddress(), _remotePort(0) -{ - reference_counting_policy().value(ACE_Event_Handler::Reference_Counting_Policy::ENABLED); - - msg_queue()->high_water_mark(8 * 1024 * 1024); - msg_queue()->low_water_mark(8 * 1024 * 1024); -} - -RealmSocket::~RealmSocket(void) -{ - if (msg_queue()) - msg_queue()->close(); - - // delete RealmSocketObject must never be called from our code. - closing_ = true; - - delete session_; - - peer().close(); -} - -int RealmSocket::open(void * arg) -{ - ACE_INET_Addr addr; - - if (peer().get_remote_addr(addr) == -1) - { - TC_LOG_ERROR("server.authserver", "Error %s while opening realm socket!", ACE_OS::strerror(errno)); - return -1; - } - - _remoteAddress = addr.get_host_addr(); - _remotePort = addr.get_port_number(); - - // Register with ACE Reactor - if (Base::open(arg) == -1) - return -1; - - if (session_) - session_->OnAccept(); - - // reactor takes care of the socket from now on - remove_reference(); - - return 0; -} - -int RealmSocket::close(u_long) -{ - shutdown(); - - closing_ = true; - - remove_reference(); - - return 0; -} - -const std::string& RealmSocket::getRemoteAddress(void) const -{ - return _remoteAddress; -} - -uint16 RealmSocket::getRemotePort(void) const -{ - return _remotePort; -} - -size_t RealmSocket::recv_len(void) const -{ - return input_buffer_.length(); -} - -bool RealmSocket::recv_soft(char *buf, size_t len) -{ - if (input_buffer_.length() < len) - return false; - - ACE_OS::memcpy(buf, input_buffer_.rd_ptr(), len); - - return true; -} - -bool RealmSocket::recv(char *buf, size_t len) -{ - bool ret = recv_soft(buf, len); - - if (ret) - recv_skip(len); - - return ret; -} - -void RealmSocket::recv_skip(size_t len) -{ - input_buffer_.rd_ptr(len); -} - -ssize_t RealmSocket::noblk_send(ACE_Message_Block &message_block) -{ - const size_t len = message_block.length(); - - if (len == 0) - return -1; - - // Try to send the message directly. -#ifdef MSG_NOSIGNAL - ssize_t n = peer().send(message_block.rd_ptr(), len, MSG_NOSIGNAL); -#else - ssize_t n = peer().send(message_block.rd_ptr(), len); -#endif // MSG_NOSIGNAL - - if (n < 0) - { - if (errno == EWOULDBLOCK) // Blocking signal - return 0; - else // Error happened - return -1; - } - else if (n == 0) - { - // Can this happen ? - return -1; - } - - // return bytes transmitted - return n; -} - -bool RealmSocket::send(const char *buf, size_t len) -{ - if (buf == NULL || len == 0) - return true; - - ACE_Data_Block db(len, ACE_Message_Block::MB_DATA, (const char*)buf, 0, 0, ACE_Message_Block::DONT_DELETE, 0); - ACE_Message_Block message_block(&db, ACE_Message_Block::DONT_DELETE, 0); - - message_block.wr_ptr(len); - - if (msg_queue()->is_empty()) - { - // Try to send it directly. - ssize_t n = noblk_send(message_block); - - if (n < 0) - return false; - - size_t un = size_t(n); - if (un == len) - return true; - - // fall down - message_block.rd_ptr(un); - } - - ACE_Message_Block* mb = message_block.clone(); - - if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value *)(&ACE_Time_Value::zero)) == -1) - { - mb->release(); - return false; - } - - if (reactor()->schedule_wakeup(this, ACE_Event_Handler::WRITE_MASK) == -1) - return false; - - return true; -} - -int RealmSocket::handle_output(ACE_HANDLE) -{ - if (closing_) - return -1; - - ACE_Message_Block* mb = 0; - - if (msg_queue()->is_empty()) - { - reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK); - return 0; - } - - if (msg_queue()->dequeue_head(mb, (ACE_Time_Value *)(&ACE_Time_Value::zero)) == -1) - return -1; - - ssize_t n = noblk_send(*mb); - - if (n < 0) - { - mb->release(); - return -1; - } - else if (size_t(n) == mb->length()) - { - mb->release(); - return 1; - } - else - { - mb->rd_ptr(n); - - if (msg_queue()->enqueue_head(mb, (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) - { - mb->release(); - return -1; - } - - return 0; - } - - ACE_NOTREACHED(return -1); -} - -int RealmSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask) -{ - // As opposed to WorldSocket::handle_close, we don't need locks here. - closing_ = true; - - if (h == ACE_INVALID_HANDLE) - peer().close_writer(); - - if (session_) - session_->OnClose(); - - reactor()->remove_handler(this, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::ALL_EVENTS_MASK); - return 0; -} - -int RealmSocket::handle_input(ACE_HANDLE) -{ - if (closing_) - return -1; - - const ssize_t space = input_buffer_.space(); - - ssize_t n = peer().recv(input_buffer_.wr_ptr(), space); - - if (n < 0) - return errno == EWOULDBLOCK ? 0 : -1; - else if (n == 0) // EOF - return -1; - - input_buffer_.wr_ptr((size_t)n); - - if (session_ != NULL) - { - session_->OnRead(); - input_buffer_.crunch(); - } - - // return 1 in case there is more data to read from OS - return n == space ? 1 : 0; -} - -void RealmSocket::set_session(Session* session) -{ - delete session_; - - session_ = session; -} diff --git a/src/server/authserver/Server/RealmSocket.h b/src/server/authserver/Server/RealmSocket.h deleted file mode 100644 index c1190d638b3..00000000000 --- a/src/server/authserver/Server/RealmSocket.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/> - * - * 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 2 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/>. - */ - -#ifndef __REALMSOCKET_H__ -#define __REALMSOCKET_H__ - -#include <ace/Synch_Traits.h> -#include <ace/Svc_Handler.h> -#include <ace/SOCK_Stream.h> -#include <ace/Message_Block.h> -#include <ace/Basic_Types.h> -#include "Common.h" - -class RealmSocket : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> -{ -private: - typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> Base; - -public: - class Session - { - public: - Session(void); - virtual ~Session(void); - - virtual void OnRead(void) = 0; - virtual void OnAccept(void) = 0; - virtual void OnClose(void) = 0; - }; - - RealmSocket(void); - virtual ~RealmSocket(void); - - size_t recv_len(void) const; - bool recv_soft(char *buf, size_t len); - bool recv(char *buf, size_t len); - void recv_skip(size_t len); - - bool send(const char *buf, size_t len); - - const std::string& getRemoteAddress(void) const; - - uint16 getRemotePort(void) const; - - virtual int open(void *); - - virtual int close(u_long); - - virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE); - virtual int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE); - - virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); - - void set_session(Session* session); - -private: - ssize_t noblk_send(ACE_Message_Block &message_block); - - ACE_Message_Block input_buffer_; - Session* session_; - std::string _remoteAddress; - uint16 _remotePort; -}; - -#endif /* __REALMSOCKET_H__ */ diff --git a/src/server/worldserver/Master.cpp b/src/server/worldserver/Master.cpp index c5424374f5a..a5230b908ef 100644 --- a/src/server/worldserver/Master.cpp +++ b/src/server/worldserver/Master.cpp @@ -40,7 +40,6 @@ #include "TCSoap.h" #include "Timer.h" #include "Util.h" -#include "AuthSocket.h" #include "RealmList.h" #include "BigNumber.h" |