diff --git a/cmake/options.cmake b/cmake/options.cmake index 8c13e295ea..17e45b9b4a 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -8,7 +8,7 @@ # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -option(SERVERS "Build worldserver and bnetserver" 1) +option(SERVERS "Build worldserver and authserver" 1) set(SCRIPTS_AVAILABLE_OPTIONS none static dynamic minimal-static minimal-dynamic) diff --git a/cmake/showoptions.cmake b/cmake/showoptions.cmake index f7fcdf46bf..1aff4aa813 100644 --- a/cmake/showoptions.cmake +++ b/cmake/showoptions.cmake @@ -16,7 +16,7 @@ message("") if(SERVERS) message("* Build world/auth : Yes (default)") else() - message("* Build world/bnetserver : No") + message("* Build world/authserver : No") endif() if(SCRIPTS AND (NOT SCRIPTS STREQUAL "none")) diff --git a/contrib/conf_merge/README b/contrib/conf_merge/README index 9e6b1e1546..974c106306 100644 --- a/contrib/conf_merge/README +++ b/contrib/conf_merge/README @@ -1,6 +1,6 @@ ==== PHP merger (index.php + merge.php) ==== -This is a PHP script for merging a new .dist file with your existing .conf file (worldserver.conf.dist and bnetserver.conf.dist) +This is a PHP script for merging a new .dist file with your existing .conf file (worldserver.conf.dist and authserver.conf.dist) It should also work with mangos dist/conf files as well. It uses sessions so it is multi user safe, it adds any options that are removed to the bottom of the file, diff --git a/doc/UnixInstall.txt b/doc/UnixInstall.txt index a1ad4c3d9a..1bf2a9c487 100644 --- a/doc/UnixInstall.txt +++ b/doc/UnixInstall.txt @@ -49,7 +49,7 @@ than where to install using flags built into our cmake files. Just open up CMakeLists.txt in the main folder and take a look at some of the flags like - SERVERS Build worldserver and bnetserver + SERVERS Build worldserver and authserver SCRIPTS Build core with scripts included TOOLS Build map/vmap extraction/assembler tools USE_SCRIPTPCH Use precompiled headers when compiling scripts diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index a9d794469e..aba4edbad8 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -30,6 +30,7 @@ endif(WIN32) add_subdirectory(database) add_subdirectory(shared) add_subdirectory(ipc) +add_subdirectory(authserver) add_subdirectory(bnetserver) add_subdirectory(game) add_subdirectory(scripts) diff --git a/src/server/authserver/Authentication/AuthCodes.cpp b/src/server/authserver/Authentication/AuthCodes.cpp new file mode 100644 index 0000000000..98d61ce2c4 --- /dev/null +++ b/src/server/authserver/Authentication/AuthCodes.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * + * 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 . + */ + +#include "AuthCodes.h" +#include + +namespace AuthHelper +{ + static RealmBuildInfo const PostBcAcceptedClientBuilds[] = + { + {15595, 4, 3, 4, ' '}, + {14545, 4, 2, 2, ' '}, + {13623, 4, 0, 6, 'a'}, + {13930, 3, 3, 5, 'a'}, // 3.3.5a China Mainland build + {12340, 3, 3, 5, 'a'}, + {11723, 3, 3, 3, 'a'}, + {11403, 3, 3, 2, ' '}, + {11159, 3, 3, 0, 'a'}, + {10505, 3, 2, 2, 'a'}, + {9947, 3, 1, 3, ' '}, + {8606, 2, 4, 3, ' '}, + {0, 0, 0, 0, ' '} // terminator + }; + + static RealmBuildInfo const PreBcAcceptedClientBuilds[] = + { + {6141, 1, 12, 3, ' '}, + {6005, 1, 12, 2, ' '}, + {5875, 1, 12, 1, ' '}, + {0, 0, 0, 0, ' '} // terminator + }; + + bool IsPreBCAcceptedClientBuild(int build) + { + for (int i = 0; PreBcAcceptedClientBuilds[i].Build; ++i) + if (PreBcAcceptedClientBuilds[i].Build == build) + return true; + + return false; + } + + bool IsPostBCAcceptedClientBuild(int build) + { + for (int i = 0; PostBcAcceptedClientBuilds[i].Build; ++i) + if (PostBcAcceptedClientBuilds[i].Build == build) + return true; + + return false; + } + + bool IsAcceptedClientBuild(int build) + { + return (IsPostBCAcceptedClientBuild(build) || IsPreBCAcceptedClientBuild(build)); + } + + RealmBuildInfo const* GetBuildInfo(int build) + { + for (int i = 0; PostBcAcceptedClientBuilds[i].Build; ++i) + if (PostBcAcceptedClientBuilds[i].Build == build) + return &PostBcAcceptedClientBuilds[i]; + + for (int i = 0; PreBcAcceptedClientBuilds[i].Build; ++i) + if (PreBcAcceptedClientBuilds[i].Build == build) + return &PreBcAcceptedClientBuilds[i]; + + return nullptr; + } + + bool IsBuildSupportingBattlenet(int build) + { + return build >= 15595; + } +} diff --git a/src/server/authserver/Authentication/AuthCodes.h b/src/server/authserver/Authentication/AuthCodes.h new file mode 100644 index 0000000000..80efec90c4 --- /dev/null +++ b/src/server/authserver/Authentication/AuthCodes.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + * + * 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 . + */ + +#ifndef _AUTHCODES_H +#define _AUTHCODES_H + +enum AuthResult +{ + WOW_SUCCESS = 0x00, + WOW_FAIL_BANNED = 0x03, + WOW_FAIL_UNKNOWN_ACCOUNT = 0x04, + WOW_FAIL_INCORRECT_PASSWORD = 0x05, + WOW_FAIL_ALREADY_ONLINE = 0x06, + WOW_FAIL_NO_TIME = 0x07, + WOW_FAIL_DB_BUSY = 0x08, + WOW_FAIL_VERSION_INVALID = 0x09, + WOW_FAIL_VERSION_UPDATE = 0x0A, + WOW_FAIL_INVALID_SERVER = 0x0B, + WOW_FAIL_SUSPENDED = 0x0C, + WOW_FAIL_FAIL_NOACCESS = 0x0D, + WOW_SUCCESS_SURVEY = 0x0E, + WOW_FAIL_PARENTCONTROL = 0x0F, + WOW_FAIL_LOCKED_ENFORCED = 0x10, + WOW_FAIL_TRIAL_ENDED = 0x11, + WOW_FAIL_USE_BATTLENET = 0x12, + WOW_FAIL_ANTI_INDULGENCE = 0x13, + WOW_FAIL_EXPIRED = 0x14, + WOW_FAIL_NO_GAME_ACCOUNT = 0x15, + WOW_FAIL_CHARGEBACK = 0x16, + WOW_FAIL_INTERNET_GAME_ROOM_WITHOUT_BNET = 0x17, + WOW_FAIL_GAME_ACCOUNT_LOCKED = 0x18, + WOW_FAIL_UNLOCKABLE_LOCK = 0x19, + WOW_FAIL_CONVERSION_REQUIRED = 0x20, + WOW_FAIL_DISCONNECTED = 0xFF +}; + +enum LoginResult +{ + LOGIN_OK = 0x00, + LOGIN_FAILED = 0x01, + LOGIN_FAILED2 = 0x02, + LOGIN_BANNED = 0x03, + LOGIN_UNKNOWN_ACCOUNT = 0x04, + LOGIN_UNKNOWN_ACCOUNT3 = 0x05, + LOGIN_ALREADYONLINE = 0x06, + LOGIN_NOTIME = 0x07, + LOGIN_DBBUSY = 0x08, + LOGIN_BADVERSION = 0x09, + LOGIN_DOWNLOAD_FILE = 0x0A, + LOGIN_FAILED3 = 0x0B, + LOGIN_SUSPENDED = 0x0C, + LOGIN_FAILED4 = 0x0D, + LOGIN_CONNECTED = 0x0E, + LOGIN_PARENTALCONTROL = 0x0F, + LOGIN_LOCKED_ENFORCED = 0x10 +}; + +enum GameAccountFlags +{ + GAMEACCOUNT_FLAG_GM = 0x00000001, + GAMEACCOUNT_FLAG_NOKICK = 0x00000002, + GAMEACCOUNT_FLAG_COLLECTOR = 0x00000004, + GAMEACCOUNT_FLAG_WOW_TRIAL = 0x00000008, + GAMEACCOUNT_FLAG_CANCELLED = 0x00000010, + GAMEACCOUNT_FLAG_IGR = 0x00000020, + GAMEACCOUNT_FLAG_WHOLESALER = 0x00000040, + GAMEACCOUNT_FLAG_PRIVILEGED = 0x00000080, + GAMEACCOUNT_FLAG_EU_FORBID_ELV = 0x00000100, + GAMEACCOUNT_FLAG_EU_FORBID_BILLING = 0x00000200, + GAMEACCOUNT_FLAG_WOW_RESTRICTED = 0x00000400, + GAMEACCOUNT_FLAG_REFERRAL = 0x00000800, + GAMEACCOUNT_FLAG_BLIZZARD = 0x00001000, + GAMEACCOUNT_FLAG_RECURRING_BILLING = 0x00002000, + GAMEACCOUNT_FLAG_NOELECTUP = 0x00004000, + GAMEACCOUNT_FLAG_KR_CERTIFICATE = 0x00008000, + GAMEACCOUNT_FLAG_EXPANSION_COLLECTOR = 0x00010000, + GAMEACCOUNT_FLAG_DISABLE_VOICE = 0x00020000, + GAMEACCOUNT_FLAG_DISABLE_VOICE_SPEAK = 0x00040000, + GAMEACCOUNT_FLAG_REFERRAL_RESURRECT = 0x00080000, + GAMEACCOUNT_FLAG_EU_FORBID_CC = 0x00100000, + GAMEACCOUNT_FLAG_OPENBETA_DELL = 0x00200000, + GAMEACCOUNT_FLAG_PROPASS = 0x00400000, + GAMEACCOUNT_FLAG_PROPASS_LOCK = 0x00800000, + GAMEACCOUNT_FLAG_PENDING_UPGRADE = 0x01000000, + GAMEACCOUNT_FLAG_RETAIL_FROM_TRIAL = 0x02000000, + GAMEACCOUNT_FLAG_EXPANSION2_COLLECTOR = 0x04000000, + GAMEACCOUNT_FLAG_OVERMIND_LINKED = 0x08000000, + GAMEACCOUNT_FLAG_DEMOS = 0x10000000, + GAMEACCOUNT_FLAG_DEATH_KNIGHT_OK = 0x20000000, +}; + +enum ExpansionFlags +{ + POST_BC_EXP_FLAG = 0x2, + PRE_BC_EXP_FLAG = 0x1, + NO_VALID_EXP_FLAG = 0x0 +}; + +struct RealmBuildInfo +{ + int Build; + int MajorVersion; + int MinorVersion; + int BugfixVersion; + int HotfixVersion; +}; + +namespace AuthHelper +{ + RealmBuildInfo const* GetBuildInfo(int build); + bool IsAcceptedClientBuild(int build); + bool IsPostBCAcceptedClientBuild(int build); + bool IsPreBCAcceptedClientBuild(int build); + bool IsBuildSupportingBattlenet(int build); +} + +#endif diff --git a/src/server/authserver/Authentication/TOTP.cpp b/src/server/authserver/Authentication/TOTP.cpp new file mode 100644 index 0000000000..76df2f653e --- /dev/null +++ b/src/server/authserver/Authentication/TOTP.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * + * 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 . + */ + +#include "TOTP.h" +#include + +int base32_decode(char const* encoded, char* result, int bufSize) +{ + // Base32 implementation + // Copyright 2010 Google Inc. + // Author: Markus Gutschke + // Licensed under the Apache License, Version 2.0 + int buffer = 0; + int bitsLeft = 0; + int count = 0; + for (const char *ptr = encoded; count < bufSize && *ptr; ++ptr) + { + char ch = *ptr; + if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') + continue; + buffer <<= 5; + + // Deal with commonly mistyped characters + if (ch == '0') + ch = 'O'; + else if (ch == '1') + ch = 'L'; + else if (ch == '8') + ch = 'B'; + + // Look up one base32 digit + if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) + ch = (ch & 0x1F) - 1; + else if (ch >= '2' && ch <= '7') + ch -= '2' - 26; + else + return -1; + + buffer |= ch; + bitsLeft += 5; + if (bitsLeft >= 8) + { + result[count++] = buffer >> (bitsLeft - 8); + bitsLeft -= 8; + } + } + + if (count < bufSize) + result[count] = '\000'; + return count; +} + +#define HMAC_RES_SIZE 20 + +namespace TOTP +{ + unsigned int GenerateToken(char const* b32key) + { + size_t keySize = strlen(b32key); + int bufsize = (keySize + 7)/8*5; + char* encoded = new char[bufsize]; + memset(encoded, 0, bufsize); + unsigned int hmacResSize = HMAC_RES_SIZE; + unsigned char hmacRes[HMAC_RES_SIZE]; + unsigned long timestamp = time(nullptr)/30; + unsigned char challenge[8]; + + for (int i = 8; i--;timestamp >>= 8) + challenge[i] = timestamp; + + base32_decode(b32key, encoded, bufsize); + HMAC(EVP_sha1(), encoded, bufsize, challenge, 8, hmacRes, &hmacResSize); + unsigned int offset = hmacRes[19] & 0xF; + unsigned int truncHash = (hmacRes[offset] << 24) | (hmacRes[offset+1] << 16 )| (hmacRes[offset+2] << 8) | (hmacRes[offset+3]); + truncHash &= 0x7FFFFFFF; + + delete[] encoded; + + return truncHash % 1000000; + } +} diff --git a/src/server/authserver/Authentication/TOTP.h b/src/server/authserver/Authentication/TOTP.h new file mode 100644 index 0000000000..2c274a222b --- /dev/null +++ b/src/server/authserver/Authentication/TOTP.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * + * 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 . + */ + +#ifndef _TOTP_H +#define _TOTP_H + +#include "openssl/hmac.h" +#include "openssl/evp.h" + +namespace TOTP +{ + unsigned int GenerateToken(char const* b32key); +} + +#endif diff --git a/src/server/authserver/CMakeLists.txt b/src/server/authserver/CMakeLists.txt new file mode 100644 index 0000000000..b56866ec0e --- /dev/null +++ b/src/server/authserver/CMakeLists.txt @@ -0,0 +1,90 @@ +# Copyright (C) 2008-2018 TrinityCore +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +########### authserver ############### + +CollectSourceFiles( + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE_SOURCES + # Exclude + ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders) + +if( WIN32 ) + list(APPEND PRIVATE_SOURCES ${sources_windows}) + if ( MSVC ) + list(APPEND PRIVATE_SOURCES authserver.rc) + endif() +endif() + +if (USE_COREPCH) + set(PRIVATE_PCH_HEADER PrecompiledHeaders/authPCH.h) +endif() + +GroupSources(${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(authserver + ${PRIVATE_SOURCES} +) + +if( NOT WIN32 ) + set_target_properties(authserver PROPERTIES + COMPILE_DEFINITIONS _TRINITY_REALM_CONFIG="${CONF_DIR}/authserver.conf" + ) +endif() + +target_link_libraries(authserver + PRIVATE + trinity-core-interface + PUBLIC + shared) + +CollectIncludeDirectories( + ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC_INCLUDES + # Exclude + ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders) + +target_include_directories(authserver + PUBLIC + ${PUBLIC_INCLUDES} + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}) + +set_target_properties(authserver + PROPERTIES + FOLDER + "server") + +if( WIN32 ) + if ( "${CMAKE_MAKE_PROGRAM}" MATCHES "MSBuild" ) + add_custom_command(TARGET authserver + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/authserver.conf.dist ${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/ + ) + elseif ( MINGW ) + add_custom_command(TARGET authserver + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/authserver.conf.dist ${CMAKE_BINARY_DIR}/bin/ + ) + endif() +endif() + +if( UNIX ) + install(TARGETS authserver DESTINATION bin) + install(FILES authserver.conf.dist DESTINATION ${CONF_DIR}) +elseif( WIN32 ) + install(TARGETS authserver DESTINATION "${CMAKE_INSTALL_PREFIX}") + install(FILES authserver.conf.dist DESTINATION "${CMAKE_INSTALL_PREFIX}") +endif() + +# Generate precompiled header +if (USE_COREPCH) + add_cxx_pch(authserver ${PRIVATE_PCH_HEADER}) +endif() diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp new file mode 100644 index 0000000000..cc97860fe9 --- /dev/null +++ b/src/server/authserver/Main.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + * + * 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 . + */ + +/** +* @file main.cpp +* @brief Authentication Server main program +* +* This file contains the main program for the +* authentication server +*/ + +#include "AppenderDB.h" +#include "AuthSocketMgr.h" +#include "Banner.h" +#include "Config.h" +#include "DatabaseEnv.h" +#include "DatabaseLoader.h" +#include "DeadlineTimer.h" +#include "GitRevision.h" +#include "GruntRealmList.h" +#include "IoContext.h" +#include "IPLocation.h" +#include "MySQLThreading.h" +#include "ProcessPriority.h" +#include "Util.h" +#include +#include +#include +#include +#include +#include +#include + +using boost::asio::ip::tcp; +using namespace boost::program_options; +namespace fs = boost::filesystem; + +#ifndef _TRINITY_REALM_CONFIG +# define _TRINITY_REALM_CONFIG "authserver.conf" +#endif + +#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS +#include "ServiceWin32.h" +char serviceName[] = "authserver"; +char serviceLongName[] = "TrinityCore auth service"; +char serviceDescription[] = "TrinityCore World of Warcraft emulator auth service"; +/* +* -1 - not in service mode +* 0 - stopped +* 1 - running +* 2 - paused +*/ +int m_ServiceStatus = -1; + +void ServiceStatusWatcher(std::weak_ptr serviceStatusWatchTimerRef, std::weak_ptr ioContextRef, boost::system::error_code const& error); +#endif + +bool StartDB(); +void StopDB(); +void SignalHandler(std::weak_ptr ioContextRef, boost::system::error_code const& error, int signalNumber); +void KeepDatabaseAliveHandler(std::weak_ptr dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error); +void BanExpiryHandler(std::weak_ptr banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error); +variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService); + +int main(int argc, char** argv) +{ + signal(SIGABRT, &Trinity::AbortHandler); + + auto configFile = fs::absolute(_TRINITY_REALM_CONFIG); + std::string configService; + auto vm = GetConsoleArguments(argc, argv, configFile, configService); + // exit if help or version is enabled + if (vm.count("help") || vm.count("version")) + return 0; + +#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS + if (configService.compare("install") == 0) + return WinServiceInstall() == true ? 0 : 1; + else if (configService.compare("uninstall") == 0) + return WinServiceUninstall() == true ? 0 : 1; + else if (configService.compare("run") == 0) + return WinServiceRun() ? 0 : 1; +#endif + + std::string configError; + if (!sConfigMgr->LoadInitial(configFile.generic_string(), + std::vector(argv, argv + argc), + configError)) + { + printf("Error in config file: %s\n", configError.c_str()); + return 1; + } + + sLog->RegisterAppender(); + sLog->Initialize(nullptr); + + Trinity::Banner::Show("authserver", + [](char const* text) + { + TC_LOG_INFO("server.authserver", "%s", text); + }, + []() + { + TC_LOG_INFO("server.authserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str()); + TC_LOG_INFO("server.authserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); + TC_LOG_INFO("server.authserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); + } + ); + + // authserver PID file creation + std::string pidFile = sConfigMgr->GetStringDefault("PidFile", ""); + if (!pidFile.empty()) + { + if (uint32 pid = CreatePIDFile(pidFile)) + TC_LOG_INFO("server.authserver", "Daemon PID: %u\n", pid); + else + { + TC_LOG_ERROR("server.authserver", "Cannot create PID file %s.\n", pidFile.c_str()); + return 1; + } + } + + // Initialize the database connection + if (!StartDB()) + return 1; + + // Load IP Location Database + sIPLocation->Load(); + + std::shared_ptr dbHandle(nullptr, [](void*) { StopDB(); }); + + std::shared_ptr ioContext = std::make_shared(); + + // Get the list of realms for the server + sGruntRealmList->Initialize(*ioContext, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20)); + + std::shared_ptr sRealmListHandle(nullptr, [](void*) { sGruntRealmList->Close(); }); + + if (sGruntRealmList->GetRealms().empty()) + { + TC_LOG_ERROR("server.authserver", "No valid realms specified."); + return 1; + } + + // Start the listening port (acceptor) for auth connections + 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 bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); + + if (!sAuthSocketMgr.StartNetwork(*ioContext, bindIp, port)) + { + TC_LOG_ERROR("server.authserver", "Failed to initialize network"); + return 1; + } + + std::shared_ptr sAuthSocketMgrHandle(nullptr, [](void*) { sAuthSocketMgr.StopNetwork(); }); + + // Set signal handlers + boost::asio::signal_set signals(*ioContext, SIGINT, SIGTERM); +#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS + signals.add(SIGBREAK); +#endif + signals.async_wait(std::bind(&SignalHandler, std::weak_ptr(ioContext), std::placeholders::_1, std::placeholders::_2)); + + // Set process priority according to configuration settings + SetProcessPriority("server.authserver", sConfigMgr->GetIntDefault(CONFIG_PROCESSOR_AFFINITY, 0), sConfigMgr->GetBoolDefault(CONFIG_HIGH_PRIORITY, false)); + + // Enabled a timed callback for handling the database keep alive ping + int32 dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30); + std::shared_ptr dbPingTimer = std::make_shared(*ioContext); + dbPingTimer->expires_after(std::chrono::minutes(dbPingInterval)); + dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, std::weak_ptr(dbPingTimer), dbPingInterval, std::placeholders::_1)); + + int32 banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60); + std::shared_ptr banExpiryCheckTimer = std::make_shared(*ioContext); + banExpiryCheckTimer->expires_after(std::chrono::seconds(banExpiryCheckInterval)); + banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, std::weak_ptr(banExpiryCheckTimer), banExpiryCheckInterval, std::placeholders::_1)); + +#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS + std::shared_ptr serviceStatusWatchTimer; + if (m_ServiceStatus != -1) + { + serviceStatusWatchTimer = std::make_shared(*ioContext); + serviceStatusWatchTimer->expires_after(std::chrono::seconds(1)); + serviceStatusWatchTimer->async_wait(std::bind(&ServiceStatusWatcher, + std::weak_ptr(serviceStatusWatchTimer), + std::weak_ptr(ioContext), + std::placeholders::_1)); + } +#endif + + // Start the io service worker loop + ioContext->run(); + + banExpiryCheckTimer->cancel(); + dbPingTimer->cancel(); + + TC_LOG_INFO("server.authserver", "Halting process..."); + + signals.cancel(); + + return 0; +} + +/// Initialize connection to the database +bool StartDB() +{ + MySQL::Library_Init(); + + // Load databases + // NOTE: While authserver is singlethreaded you should keep synch_threads == 1. + // Increasing it is just silly since only 1 will be used ever. + DatabaseLoader loader("server.authserver", DatabaseLoader::DATABASE_NONE); + loader + .AddDatabase(LoginDatabase, "Login"); + + if (!loader.Load()) + return false; + + TC_LOG_INFO("server.authserver", "Started auth database connection pool."); + sLog->SetRealmId(0); // Enables DB appenders when realm is set. + return true; +} + +/// Close the connection to the database +void StopDB() +{ + LoginDatabase.Close(); + MySQL::Library_End(); +} + +void SignalHandler(std::weak_ptr ioContextRef, boost::system::error_code const& error, int /*signalNumber*/) +{ + if (!error) + if (std::shared_ptr ioContext = ioContextRef.lock()) + ioContext->stop(); +} + +void KeepDatabaseAliveHandler(std::weak_ptr dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error) +{ + if (!error) + { + if (std::shared_ptr dbPingTimer = dbPingTimerRef.lock()) + { + TC_LOG_INFO("server.authserver", "Ping MySQL to keep connection alive"); + LoginDatabase.KeepAlive(); + + dbPingTimer->expires_after(std::chrono::minutes(dbPingInterval)); + dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, dbPingTimerRef, dbPingInterval, std::placeholders::_1)); + } + } +} + +void BanExpiryHandler(std::weak_ptr banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error) +{ + if (!error) + { + if (std::shared_ptr banExpiryCheckTimer = banExpiryCheckTimerRef.lock()) + { + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); + + banExpiryCheckTimer->expires_after(std::chrono::seconds(banExpiryCheckInterval)); + banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, banExpiryCheckTimerRef, banExpiryCheckInterval, std::placeholders::_1)); + } + } +} + +#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS +void ServiceStatusWatcher(std::weak_ptr serviceStatusWatchTimerRef, std::weak_ptr ioContextRef, boost::system::error_code const& error) +{ + if (!error) + { + if (std::shared_ptr ioContext = ioContextRef.lock()) + { + if (m_ServiceStatus == 0) + ioContext->stop(); + else if (std::shared_ptr serviceStatusWatchTimer = serviceStatusWatchTimerRef.lock()) + { + serviceStatusWatchTimer->expires_after(std::chrono ::seconds(1)); + serviceStatusWatchTimer->async_wait(std::bind(&ServiceStatusWatcher, serviceStatusWatchTimerRef, ioContextRef, std::placeholders::_1)); + } + } + } +} +#endif + +variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService) +{ + options_description all("Allowed options"); + all.add_options() + ("help,h", "print usage message") + ("version,v", "print version build info") + ("config,c", value(&configFile)->default_value(fs::absolute(_TRINITY_REALM_CONFIG)), + "use as configuration file") + ; +#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS + options_description win("Windows platform specific options"); + win.add_options() + ("service,s", value(&configService)->default_value(""), "Windows service options: [install | uninstall]") + ; + + all.add(win); +#else + (void)configService; +#endif + variables_map variablesMap; + try + { + store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), variablesMap); + notify(variablesMap); + } + catch (std::exception& e) + { + std::cerr << e.what() << "\n"; + } + + if (variablesMap.count("help")) + std::cout << all << "\n"; + else if (variablesMap.count("version")) + std::cout << GitRevision::GetFullVersion() << "\n"; + + return variablesMap; +} diff --git a/src/server/authserver/PrecompiledHeaders/authPCH.h b/src/server/authserver/PrecompiledHeaders/authPCH.h new file mode 100644 index 0000000000..b2be78a8a3 --- /dev/null +++ b/src/server/authserver/PrecompiledHeaders/authPCH.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * + * 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 . + */ + +#include "Common.h" +#include "Configuration/Config.h" +#include "Database/DatabaseEnv.h" +#include "Log.h" +#include "GruntRealmList.h" +#include "AuthSession.h" diff --git a/src/server/authserver/Realms/GruntRealmList.cpp b/src/server/authserver/Realms/GruntRealmList.cpp new file mode 100644 index 0000000000..8064c4cc09 --- /dev/null +++ b/src/server/authserver/Realms/GruntRealmList.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + * + * 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 . + */ + +#include "GruntRealmList.h" +#include "DatabaseEnv.h" +#include "DeadlineTimer.h" +#include "IoContext.h" +#include "Log.h" +#include "Resolver.h" +#include "Util.h" +#include + +GruntRealmList::GruntRealmList() : _updateInterval(0) +{ +} + +GruntRealmList::~GruntRealmList() +{ +} + +GruntRealmList* GruntRealmList::Instance() +{ + static GruntRealmList instance; + return &instance; +} + +// Load the realm list from the database +void GruntRealmList::Initialize(Trinity::Asio::IoContext& ioContext, uint32 updateInterval) +{ + _updateInterval = updateInterval; + _updateTimer = Trinity::make_unique(ioContext); + _resolver = Trinity::make_unique(ioContext); + + // Get the content of the realmlist table in the database + UpdateRealms(boost::system::error_code()); +} + +void GruntRealmList::Close() +{ + _updateTimer->cancel(); +} + +void GruntRealmList::UpdateRealm(Battlenet::RealmHandle const& id, uint32 build, std::string const& name, + boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr, boost::asio::ip::address&& localSubmask, + uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population) +{ + // Create new if not exist or update existed + Realm& realm = _realms[id]; + + // grunt server doesn't use these values, but keep them initialized + realm.Updated = false; + realm.Keep = true; + + realm.Id = id; + realm.Build = build; + realm.Name = name; + realm.Type = icon; + realm.Flags = flag; + realm.Timezone = timezone; + realm.AllowedSecurityLevel = allowedSecurityLevel; + realm.PopulationLevel = population; + if (!realm.ExternalAddress || *realm.ExternalAddress != address) + realm.ExternalAddress = Trinity::make_unique(std::move(address)); + if (!realm.LocalAddress || *realm.LocalAddress != localAddr) + realm.LocalAddress = Trinity::make_unique(std::move(localAddr)); + if (!realm.LocalSubnetMask || *realm.LocalSubnetMask != localSubmask) + realm.LocalSubnetMask = Trinity::make_unique(std::move(localSubmask)); + realm.Port = port; +} + +void GruntRealmList::UpdateRealms(boost::system::error_code const& error) +{ + if (error) + return; + + TC_LOG_DEBUG("server.authserver", "Updating Realm List..."); + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + std::map existingRealms; + for (auto const& p : _realms) + existingRealms[p.first] = p.second.Name; + + _realms.clear(); + + // Circle through results and add them to the realm map + if (result) + { + do + { + try + { + Field* fields = result->Fetch(); + uint32 realmId = fields[0].GetUInt32(); + std::string name = fields[1].GetString(); + std::string externalAddressString = fields[2].GetString(); + std::string localAddressString = fields[3].GetString(); + std::string localSubmaskString = fields[4].GetString(); + + Optional externalAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), externalAddressString, ""); + if (!externalAddress) + { + TC_LOG_ERROR("realmlist", "Could not resolve address %s", externalAddressString.c_str()); + continue; + } + + Optional localAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), localAddressString, ""); + if (!localAddress) + { + TC_LOG_ERROR("realmlist", "Could not resolve address %s", localAddressString.c_str()); + continue; + } + + Optional localSubmask = _resolver->Resolve(boost::asio::ip::tcp::v4(), localSubmaskString, ""); + if (!localSubmask) + { + TC_LOG_ERROR("realmlist", "Could not resolve address %s", localSubmaskString.c_str()); + continue; + } + + uint16 port = fields[5].GetUInt16(); + uint8 icon = fields[6].GetUInt8(); + if (icon == REALM_TYPE_FFA_PVP) + icon = REALM_TYPE_PVP; + if (icon >= MAX_CLIENT_REALM_TYPE) + icon = REALM_TYPE_NORMAL; + RealmFlags flag = RealmFlags(fields[7].GetUInt8()); + uint8 timezone = fields[8].GetUInt8(); + uint8 allowedSecurityLevel = fields[9].GetUInt8(); + float pop = fields[10].GetFloat(); + uint32 build = fields[11].GetUInt32(); + uint8 region = fields[12].GetUInt8(); + uint8 battlegroup = fields[13].GetUInt8(); + + Battlenet::RealmHandle id{ region, battlegroup, realmId }; + + UpdateRealm(id, build, name, externalAddress->address(), localAddress->address(), localSubmask->address(), port, icon, flag, + timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop); + + if (!existingRealms.count(id)) + TC_LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port); + else + TC_LOG_DEBUG("server.authserver", "Updating realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port); + + existingRealms.erase(id); + } + catch (std::exception& ex) + { + TC_LOG_ERROR("server.authserver", "GruntRealmList::UpdateRealms has thrown an exception: %s", ex.what()); + ABORT(); + } + } + while (result->NextRow()); + } + + for (auto itr = existingRealms.begin(); itr != existingRealms.end(); ++itr) + TC_LOG_INFO("server.authserver", "Removed realm \"%s\".", itr->second.c_str()); + + if (_updateInterval) + { + _updateTimer->expires_after(std::chrono::seconds(_updateInterval)); + _updateTimer->async_wait(std::bind(&GruntRealmList::UpdateRealms, this, std::placeholders::_1)); + } +} + +Realm const* GruntRealmList::GetRealm(Battlenet::RealmHandle const& id) const +{ + auto itr = _realms.find(id); + if (itr != _realms.end()) + return &itr->second; + + return nullptr; +} diff --git a/src/server/authserver/Realms/GruntRealmList.h b/src/server/authserver/Realms/GruntRealmList.h new file mode 100644 index 0000000000..ceefdcc5b1 --- /dev/null +++ b/src/server/authserver/Realms/GruntRealmList.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + * + * 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 . + */ + +#ifndef GruntRealmList_h__ +#define GruntRealmList_h__ + +#include "Define.h" +#include "Realm.h" +#include +#include +#include + +namespace boost +{ + namespace system + { + class error_code; + } +} + +namespace Trinity +{ + namespace Asio + { + class IoContext; + class DeadlineTimer; + } +} + +/// Storage object for the list of realms on the server +class GruntRealmList +{ + public: + typedef std::map RealmMap; + + static GruntRealmList* Instance(); + + ~GruntRealmList(); + + void Initialize(Trinity::Asio::IoContext& ioContext, uint32 updateInterval); + void Close(); + + RealmMap const& GetRealms() const { return _realms; } + Realm const* GetRealm(Battlenet::RealmHandle const& id) const; + + private: + GruntRealmList(); + + void UpdateRealms(boost::system::error_code const& error); + void UpdateRealm(Battlenet::RealmHandle const& id, uint32 build, std::string const& name, + boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr, boost::asio::ip::address&& localSubmask, + uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population); + + RealmMap _realms; + uint32 _updateInterval; + std::unique_ptr _updateTimer; + std::unique_ptr _resolver; +}; + +#define sGruntRealmList GruntRealmList::Instance() + +#endif // GruntRealmList_h__ diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp new file mode 100644 index 0000000000..31853d4f98 --- /dev/null +++ b/src/server/authserver/Server/AuthSession.cpp @@ -0,0 +1,924 @@ +/* +* Copyright (C) 2008-2018 TrinityCore +* Copyright (C) 2005-2009 MaNGOS +* +* 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 . +*/ + +#include "AuthSession.h" +#include "AuthCodes.h" +#include "ByteBuffer.h" +#include "Config.h" +#include "DatabaseEnv.h" +#include "Errors.h" +#include "IPLocation.h" +#include "GruntRealmList.h" +#include "Log.h" +#include "Realm.h" +#include "SHA1.h" +#include "TOTP.h" +#include "Util.h" +#include +#include + +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 +}; + +#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 AccountFlags; + uint32 SurveyId; + 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) + +enum class BufferSizes : uint32 +{ + SRP_6_V = 0x20, + SRP_6_S = 0x20, +}; + +#define MAX_ACCEPTED_CHALLENGE_SIZE (sizeof(AUTH_LOGON_CHALLENGE_C) + 16) + +#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4 +#define REALM_LIST_PACKET_SIZE 5 + +std::unordered_map AuthSession::InitHandlers() +{ + std::unordered_map handlers; + + handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleLogonChallenge }; + handlers[AUTH_LOGON_PROOF] = { STATUS_LOGON_PROOF, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof }; + handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge }; + handlers[AUTH_RECONNECT_PROOF] = { STATUS_RECONNECT_PROOF, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof }; + handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList }; + + return handlers; +} + +std::unordered_map const Handlers = AuthSession::InitHandlers(); + +void AccountInfo::LoadResult(Field* fields) +{ + // 0 1 2 3 4 5 6 + //SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, + // 7 8 9 10 11 12 + // ab.unbandate = ab.bandate, aa.gmlevel, a.token_key, a.sha_pass_hash, a.v, a.s + //FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ? + + Id = fields[0].GetUInt32(); + Login = fields[1].GetString(); + IsLockedToIP = fields[2].GetBool(); + LockCountry = fields[3].GetString(); + LastIP = fields[4].GetString(); + FailedLogins = fields[5].GetUInt32(); + IsBanned = fields[6].GetUInt64() != 0; + IsPermanenetlyBanned = fields[7].GetUInt64() != 0; + SecurityLevel = AccountTypes(fields[8].GetUInt8()); + + // Use our own uppercasing of the account name instead of using UPPER() in mysql query + // This is how the account was created in the first place and changing it now would result in breaking + // login for all accounts having accented characters in their name + Utf8ToUpperOnlyLatin(Login); +} + +AuthSession::AuthSession(tcp::socket&& socket) : Socket(std::move(socket)), +_status(STATUS_CHALLENGE), _build(0), _expversion(0) +{ + N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); + g.SetDword(7); +} + +void AuthSession::Start() +{ + std::string ip_address = GetRemoteIpAddress().to_string(); + TC_LOG_TRACE("session", "Accepted connection from %s", ip_address.c_str()); + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); + stmt->setString(0, ip_address); + + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1))); +} + +bool AuthSession::Update() +{ + if (!AuthSocket::Update()) + return false; + + _queryProcessor.ProcessReadyCallbacks(); + + return true; +} + +void AuthSession::CheckIpCallback(PreparedQueryResult result) +{ + if (result) + { + bool banned = false; + do + { + Field* fields = result->Fetch(); + if (fields[0].GetUInt64() != 0) + banned = true; + + } while (result->NextRow()); + + if (banned) + { + ByteBuffer pkt; + pkt << uint8(AUTH_LOGON_CHALLENGE); + pkt << uint8(0x00); + pkt << uint8(WOW_FAIL_BANNED); + SendPacket(pkt); + TC_LOG_DEBUG("session", "[AuthSession::CheckIpCallback] Banned ip '%s:%d' tries to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort()); + return; + } + } + + AsyncRead(); +} + +void AuthSession::ReadHandler() +{ + MessageBuffer& packet = GetReadBuffer(); + while (packet.GetActiveSize()) + { + uint8 cmd = packet.GetReadPointer()[0]; + auto itr = Handlers.find(cmd); + if (itr == Handlers.end()) + { + // well we dont handle this, lets just ignore it + packet.Reset(); + break; + } + + if (_status != itr->second.status) + { + CloseSocket(); + return; + } + + uint16 size = uint16(itr->second.packetSize); + if (packet.GetActiveSize() < size) + break; + + if (cmd == AUTH_LOGON_CHALLENGE || cmd == AUTH_RECONNECT_CHALLENGE) + { + sAuthLogonChallenge_C* challenge = reinterpret_cast(packet.GetReadPointer()); + size += challenge->size; + if (size > MAX_ACCEPTED_CHALLENGE_SIZE) + { + CloseSocket(); + return; + } + } + + if (packet.GetActiveSize() < size) + break; + + if (!(*this.*itr->second.handler)()) + { + CloseSocket(); + return; + } + + packet.ReadCompleted(size); + } + + AsyncRead(); +} + +void AuthSession::SendPacket(ByteBuffer& packet) +{ + if (!IsOpen()) + return; + + if (!packet.empty()) + { + MessageBuffer buffer; + buffer.Write(packet.contents(), packet.size()); + QueuePacket(std::move(buffer)); + } +} + +bool AuthSession::HandleLogonChallenge() +{ + _status = STATUS_CLOSED; + + sAuthLogonChallenge_C* challenge = reinterpret_cast(GetReadBuffer().GetReadPointer()); + if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len) + return false; + + std::string login((char const*)challenge->I, challenge->I_len); + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] '%s'", login.c_str()); + + _build = challenge->build; + _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); + std::array os; + os.fill('\0'); + memcpy(os.data(), challenge->os, sizeof(challenge->os)); + _os = os.data(); + + // Restore string order as its byte order is reversed + std::reverse(_os.begin(), _os.end()); + + _localizationName.resize(4); + for (int i = 0; i < 4; ++i) + _localizationName[i] = challenge->country[4 - i - 1]; + + // Get the account details from the account table + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE); + stmt->setString(0, login); + + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1))); + return true; +} + +void AuthSession::LogonChallengeCallback(PreparedQueryResult result) +{ + ByteBuffer pkt; + pkt << uint8(AUTH_LOGON_CHALLENGE); + pkt << uint8(0x00); + + if (!result) + { + pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); + SendPacket(pkt); + return; + } + + Field* fields = result->Fetch(); + + _accountInfo.LoadResult(fields); + + std::string ipAddress = GetRemoteIpAddress().to_string(); + uint16 port = GetRemotePort(); + + // If the IP is 'locked', check that the player comes indeed from the correct IP address + if (_accountInfo.IsLockedToIP) + { + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountInfo.Login.c_str(), _accountInfo.LastIP.c_str(), ipAddress.c_str()); + if (_accountInfo.LastIP != ipAddress) + { + pkt << uint8(WOW_FAIL_LOCKED_ENFORCED); + SendPacket(pkt); + return; + } + } + else + { + if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(ipAddress)) + _ipCountry = location->CountryCode; + + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _accountInfo.Login.c_str()); + if (_accountInfo.LockCountry.empty() || _accountInfo.LockCountry == "00") + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _accountInfo.Login.c_str()); + else if (!_accountInfo.LockCountry.empty() && !_ipCountry.empty()) + { + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _accountInfo.Login.c_str(), _accountInfo.LockCountry.c_str(), _ipCountry.c_str()); + if (_ipCountry != _accountInfo.LockCountry) + { + pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK); + SendPacket(pkt); + return; + } + } + } + + // If the account is banned, reject the logon attempt + if (_accountInfo.IsBanned) + { + if (_accountInfo.IsPermanenetlyBanned) + { + pkt << uint8(WOW_FAIL_BANNED); + SendPacket(pkt); + TC_LOG_INFO("server.authserver.banned", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", ipAddress.c_str(), port, _accountInfo.Login.c_str()); + return; + } + else + { + pkt << uint8(WOW_FAIL_SUSPENDED); + SendPacket(pkt); + TC_LOG_INFO("server.authserver.banned", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", ipAddress.c_str(), port, _accountInfo.Login.c_str()); + return; + } + } + + // Get the password from the account table, upper it, and make the SRP6 calculation + std::string rI = fields[10].GetString(); + + // Don't calculate (v, s) if there are already some in the database + std::string databaseV = fields[11].GetString(); + std::string databaseS = fields[12].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() != size_t(BufferSizes::SRP_6_V) * 2 || databaseS.size() != size_t(BufferSizes::SRP_6_S) * 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 (fields[13].GetUInt32() && AuthHelper::IsBuildSupportingBattlenet(_build)) + pkt << uint8(WOW_FAIL_USE_BATTLENET); + else if (AuthHelper::IsAcceptedClientBuild(_build)) + { + pkt << uint8(WOW_SUCCESS); + _status = STATUS_LOGON_PROOF; + } + 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(1).get(), 1); + pkt << uint8(32); + pkt.append(N.AsByteArray(32).get(), 32); + pkt.append(s.AsByteArray(int32(BufferSizes::SRP_6_S)).get(), size_t(BufferSizes::SRP_6_S)); // 32 bytes + pkt.append(unk3.AsByteArray(16).get(), 16); + uint8 securityFlags = 0; + + // Check if token is used + _tokenKey = fields[9].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); + + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%s' locale (%u)", + ipAddress.c_str(), port, _accountInfo.Login.c_str(), _localizationName.c_str(), GetLocaleByName(_localizationName)); + + SendPacket(pkt); +} + +// Logon Proof command handler +bool AuthSession::HandleLogonProof() +{ + TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof"); + _status = STATUS_CLOSED; + + // Read the packet + sAuthLogonProof_C *logonProof = reinterpret_cast(GetReadBuffer().GetReadPointer()); + + // 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 % N).IsZero()) + return false; + + SHA1Hash sha; + sha.UpdateBigNumbers(&A, &B, nullptr); + 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, nullptr); + sha.Finalize(); + memcpy(hash, sha.GetDigest(), 20); + sha.Initialize(); + sha.UpdateBigNumbers(&g, nullptr); + sha.Finalize(); + + for (int i = 0; i < 20; ++i) + hash[i] ^= sha.GetDigest()[i]; + + BigNumber t3; + t3.SetBinary(hash, 20); + + sha.Initialize(); + sha.UpdateData(_accountInfo.Login); + sha.Finalize(); + uint8 t4[SHA_DIGEST_LENGTH]; + memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH); + + sha.Initialize(); + sha.UpdateBigNumbers(&t3, nullptr); + sha.UpdateData(t4, SHA_DIGEST_LENGTH); + sha.UpdateBigNumbers(&s, &A, &B, &K, nullptr); + sha.Finalize(); + BigNumber M; + M.SetBinary(sha.GetDigest(), sha.GetLength()); + + // Check if SRP6 results match (password is correct), else send an error + if (!memcmp(M.AsByteArray(sha.GetLength()).get(), logonProof->M1, 20)) + { + // Check auth token + if ((logonProof->securityFlags & 0x04) || !_tokenKey.empty()) + { + uint8 size = *(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C)); + std::string token(reinterpret_cast(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size); + GetReadBuffer().ReadCompleted(sizeof(size) + size); + uint32 validToken = TOTP::GenerateToken(_tokenKey.c_str()); + _tokenKey.clear(); + uint32 incomingToken = atoi(token.c_str()); + if (validToken != incomingToken) + { + ByteBuffer packet; + packet << uint8(AUTH_LOGON_PROOF); + packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); + packet << uint8(3); + packet << uint8(0); + SendPacket(packet); + return true; + } + } + + TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.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 + + LoginDatabasePreparedStatement*stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); + stmt->setString(0, K.AsHexStr()); + stmt->setString(1, GetRemoteIpAddress().to_string()); + stmt->setUInt32(2, GetLocaleByName(_localizationName)); + stmt->setString(3, _os); + stmt->setString(4, _accountInfo.Login); + LoginDatabase.DirectExecute(stmt); + + // Finish SRP6 and send the final result to the client + sha.Initialize(); + sha.UpdateBigNumbers(&A, &M, &K, nullptr); + sha.Finalize(); + + ByteBuffer packet; + 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.AccountFlags = GAMEACCOUNT_FLAG_PROPASS_LOCK; + proof.SurveyId = 0; + proof.unk3 = 0; + + packet.resize(sizeof(proof)); + std::memcpy(packet.contents(), &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; + + packet.resize(sizeof(proof)); + std::memcpy(packet.contents(), &proof, sizeof(proof)); + } + + SendPacket(packet); + _status = STATUS_AUTHED; + } + else + { + ByteBuffer packet; + packet << uint8(AUTH_LOGON_PROOF); + packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); + packet << uint8(3); + packet << uint8(0); + SendPacket(packet); + + TC_LOG_INFO("server.authserver.hack", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", + GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str()); + + uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0); + + // We can not include the failed account login hook. However, this is a workaround to still log this. + if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false)) + { + LoginDatabasePreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING); + logstmt->setUInt32(0, _accountInfo.Id); + logstmt->setString(1, GetRemoteIpAddress().to_string()); + logstmt->setString(2, "Logged on failed AccountLogin due wrong password"); + + LoginDatabase.Execute(logstmt); + } + + if (MaxWrongPassCount > 0) + { + //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS); + stmt->setString(0, _accountInfo.Login); + LoginDatabase.Execute(stmt); + + if (++_accountInfo.FailedLogins >= MaxWrongPassCount) + { + uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600); + bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false); + + if (WrongPassBanType) + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED); + stmt->setUInt32(0, _accountInfo.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().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str(), WrongPassBanTime, _accountInfo.FailedLogins); + } + else + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED); + stmt->setString(0, GetRemoteIpAddress().to_string()); + 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().to_string().c_str(), GetRemotePort(), WrongPassBanTime, _accountInfo.Login.c_str(), _accountInfo.FailedLogins); + } + } + } + } + + return true; +} + +bool AuthSession::HandleReconnectChallenge() +{ + _status = STATUS_CLOSED; + + sAuthLogonChallenge_C* challenge = reinterpret_cast(GetReadBuffer().GetReadPointer()); + if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len) + return false; + + std::string login((char const*)challenge->I, challenge->I_len); + TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] '%s'", login.c_str()); + + _build = challenge->build; + _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); + std::array os; + os.fill('\0'); + memcpy(os.data(), challenge->os, sizeof(challenge->os)); + _os = os.data(); + + // Restore string order as its byte order is reversed + std::reverse(_os.begin(), _os.end()); + + _localizationName.resize(4); + for (int i = 0; i < 4; ++i) + _localizationName[i] = challenge->country[4 - i - 1]; + + // Get the account details from the account table + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE); + stmt->setString(0, login); + + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1))); + return true; +} + +void AuthSession::ReconnectChallengeCallback(PreparedQueryResult result) +{ + ByteBuffer pkt; + pkt << uint8(AUTH_RECONNECT_CHALLENGE); + + if (!result) + { + pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); + SendPacket(pkt); + return; + } + + Field* fields = result->Fetch(); + + _accountInfo.LoadResult(fields); + K.SetHexStr(fields[9].GetCString()); + _reconnectProof.SetRand(16 * 8); + _status = STATUS_RECONNECT_PROOF; + + pkt << uint8(WOW_SUCCESS); + pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random + pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros + + SendPacket(pkt); +} + +bool AuthSession::HandleReconnectProof() +{ + TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof"); + _status = STATUS_CLOSED; + + sAuthReconnectProof_C *reconnectProof = reinterpret_cast(GetReadBuffer().GetReadPointer()); + + if (_accountInfo.Login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) + return false; + + BigNumber t1; + t1.SetBinary(reconnectProof->R1, 16); + + SHA1Hash sha; + sha.Initialize(); + sha.UpdateData(_accountInfo.Login); + sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, nullptr); + 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 + SendPacket(pkt); + _status = STATUS_AUTHED; + return true; + } + else + { + TC_LOG_ERROR("server.authserver.hack", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", GetRemoteIpAddress().to_string().c_str(), + GetRemotePort(), _accountInfo.Login.c_str()); + return false; + } +} + +bool AuthSession::HandleRealmList() +{ + TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList"); + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS); + stmt->setUInt32(0, _accountInfo.Id); + + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1))); + _status = STATUS_WAITING_FOR_REALM_LIST; + return true; +} + +void AuthSession::RealmListCallback(PreparedQueryResult result) +{ + std::map characterCounts; + if (result) + { + do + { + Field* fields = result->Fetch(); + characterCounts[fields[0].GetUInt32()] = fields[1].GetUInt8(); + } while (result->NextRow()); + } + + // 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 (auto const& i : sGruntRealmList->GetRealms()) + { + Realm const& realm = i.second; + // don't work with realms which not compatible with the client + bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.Build == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.Build)); + + // No SQL injection. id of realm is controlled by the database. + uint32 flag = realm.Flags; + RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm.Build); + 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 = realm.Name; + if (_expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD) + { + std::ostringstream ss; + ss << name << " (" << buildInfo->MajorVersion << '.' << buildInfo->MinorVersion << '.' << buildInfo->BugfixVersion << ')'; + name = ss.str(); + } + + uint8 lock = (realm.AllowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0; + + pkt << uint8(realm.Type); // realm type + if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients + pkt << uint8(lock); // if 1, then realm locked + pkt << uint8(flag); // RealmFlags + pkt << name; + pkt << boost::lexical_cast(realm.GetAddressForClient(GetRemoteIpAddress())); + pkt << float(realm.PopulationLevel); + pkt << uint8(characterCounts[realm.Id.Realm]); + pkt << uint8(realm.Timezone); // realm category + if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients + pkt << uint8(realm.Id.Realm); + 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 + SendPacket(hdr); + + _status = STATUS_AUTHED; +} + +// Make the SRP6 calculation from hash in dB +void AuthSession::SetVSFields(const std::string& rI) +{ + s.SetRand(int32(BufferSizes::SRP_6_S) * 8); + + BigNumber I; + I.SetHexStr(rI.c_str()); + + // In case of leading zeros in the rI hash, restore them + uint8 mDigest[SHA_DIGEST_LENGTH]; + memcpy(mDigest, I.AsByteArray(SHA_DIGEST_LENGTH).get(), SHA_DIGEST_LENGTH); + + std::reverse(mDigest, mDigest + SHA_DIGEST_LENGTH); + + SHA1Hash sha; + sha.UpdateData(s.AsByteArray(uint32(BufferSizes::SRP_6_S)).get(), (uint32(BufferSizes::SRP_6_S))); + 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) + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS); + stmt->setString(0, v.AsHexStr()); + stmt->setString(1, s.AsHexStr()); + stmt->setString(2, _accountInfo.Login); + LoginDatabase.Execute(stmt); +} diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h new file mode 100644 index 0000000000..6ab94b82b1 --- /dev/null +++ b/src/server/authserver/Server/AuthSession.h @@ -0,0 +1,121 @@ +/* +* Copyright (C) 2008-2018 TrinityCore +* Copyright (C) 2005-2009 MaNGOS +* +* 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 . +*/ + +#ifndef __AUTHSESSION_H__ +#define __AUTHSESSION_H__ + +#include "AsyncCallbackProcessor.h" +#include "DatabaseEnvFwd.h" +#include "Common.h" +#include "BigNumber.h" +#include "Socket.h" +#include +#include + +using boost::asio::ip::tcp; + +class ByteBuffer; +class Field; +struct AuthHandler; + +enum AuthStatus +{ + STATUS_CHALLENGE = 0, + STATUS_LOGON_PROOF, + STATUS_RECONNECT_PROOF, + STATUS_AUTHED, + STATUS_WAITING_FOR_REALM_LIST, + STATUS_CLOSED +}; + +struct AccountInfo +{ + void LoadResult(Field* fields); + + uint32 Id = 0; + std::string Login; + bool IsLockedToIP = false; + std::string LockCountry; + std::string LastIP; + uint32 FailedLogins = 0; + bool IsBanned = false; + bool IsPermanenetlyBanned = false; + AccountTypes SecurityLevel = SEC_PLAYER; + std::string TokenKey; +}; + +class AuthSession : public Socket +{ + typedef Socket AuthSocket; + +public: + static std::unordered_map InitHandlers(); + + AuthSession(tcp::socket&& socket); + + void Start() override; + bool Update() override; + + void SendPacket(ByteBuffer& packet); + +protected: + void ReadHandler() override; + +private: + bool HandleLogonChallenge(); + bool HandleLogonProof(); + bool HandleReconnectChallenge(); + bool HandleReconnectProof(); + bool HandleRealmList(); + + void CheckIpCallback(PreparedQueryResult result); + void LogonChallengeCallback(PreparedQueryResult result); + void ReconnectChallengeCallback(PreparedQueryResult result); + void RealmListCallback(PreparedQueryResult result); + + void SetVSFields(const std::string& rI); + + BigNumber N, s, g, v; + BigNumber b, B; + BigNumber K; + BigNumber _reconnectProof; + + AuthStatus _status; + AccountInfo _accountInfo; + std::string _tokenKey; + std::string _localizationName; + std::string _os; + std::string _ipCountry; + uint16 _build; + uint8 _expversion; + + QueryCallbackProcessor _queryProcessor; +}; + +#pragma pack(push, 1) + +struct AuthHandler +{ + AuthStatus status; + size_t packetSize; + bool (AuthSession::*handler)(); +}; + +#pragma pack(pop) + +#endif diff --git a/src/server/authserver/Server/AuthSocketMgr.h b/src/server/authserver/Server/AuthSocketMgr.h new file mode 100644 index 0000000000..04d65d396b --- /dev/null +++ b/src/server/authserver/Server/AuthSocketMgr.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * + * 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 . + */ + +#ifndef AuthSocketMgr_h__ +#define AuthSocketMgr_h__ + +#include "SocketMgr.h" +#include "AuthSession.h" + +class AuthSocketMgr : public SocketMgr +{ + typedef SocketMgr BaseSocketMgr; + +public: + static AuthSocketMgr& Instance() + { + static AuthSocketMgr instance; + return instance; + } + + bool StartNetwork(Trinity::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int threadCount = 1) override + { + if (!BaseSocketMgr::StartNetwork(ioContext, bindIp, port, threadCount)) + return false; + + _acceptor->AsyncAcceptWithCallback<&AuthSocketMgr::OnSocketAccept>(); + return true; + } + +protected: + NetworkThread* CreateThreads() const override + { + return new NetworkThread[1]; + } + + static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex) + { + Instance().OnSocketOpen(std::forward(sock), threadIndex); + } +}; + +#define sAuthSocketMgr AuthSocketMgr::Instance() + +#endif // AuthSocketMgr_h__ diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist new file mode 100644 index 0000000000..efde048726 --- /dev/null +++ b/src/server/authserver/authserver.conf.dist @@ -0,0 +1,359 @@ +############################################### +# Trinity Core Auth Server configuration file # +############################################### +[authserver] + +################################################################################################### +# SECTION INDEX +# +# EXAMPLE CONFIG +# AUTH SERVER SETTINGS +# MYSQL SETTINGS +# UPDATE SETTINGS +# LOGGING SYSTEM SETTINGS +# +################################################################################################### + +################################################################################################### +# EXAMPLE CONFIG +# +# Variable +# Description: Brief description what the variable is doing. +# Important: Annotation for important things about this variable. +# Example: "Example, i.e. if the value is a string" +# Default: 10 - (Enabled|Comment|Variable name in case of grouped config options) +# 0 - (Disabled|Comment|Variable name in case of grouped config options) +# +# Note to developers: +# - Copy this example to keep the formatting. +# - Line breaks should be at column 100. +################################################################################################### + +################################################################################################### +# AUTH SERVER SETTINGS +# +# LogsDir +# Description: Logs directory setting. +# Important: LogsDir needs to be quoted, as the string might contain space characters. +# Logs directory must exists, or log file creation will be disabled. +# Default: "" - (Log files will be stored in the current path) + +LogsDir = "" + +# +# MaxPingTime +# Description: Time (in minutes) between database pings. +# Default: 30 + +MaxPingTime = 30 + +# +# RealmServerPort +# Description: TCP port to reach the auth server. +# Default: 3724 + +RealmServerPort = 3724 + +# +# +# BindIP +# Description: Bind auth server to IP/hostname +# Default: "0.0.0.0" - (Bind to all IPs on the system) + +BindIP = "0.0.0.0" + +# +# PidFile +# Description: Auth server PID file. +# Example: "./authserver.pid" - (Enabled) +# Default: "" - (Disabled) + +PidFile = "" + +# +# UseProcessors +# Description: Processors mask for Windows and Linux based multi-processor systems. +# Example: A computer with 2 CPUs: +# 1 - 1st CPU only, 2 - 2nd CPU only, 3 - 1st and 2nd CPU, because 1 | 2 is 3 +# Default: 0 - (Selected by OS) +# 1+ - (Bit mask value of selected processors) + +UseProcessors = 0 + +# +# ProcessPriority +# Description: Process priority setting for Windows and Linux based systems. +# Details: On Linux, a nice value of -15 is used. (requires superuser). On Windows, process is set to HIGH class. +# Default: 0 - (Normal) +# 1 - (High) + +ProcessPriority = 0 + +# +# RealmsStateUpdateDelay +# Description: Time (in seconds) between realm list updates. +# Default: 20 - (Enabled) +# 0 - (Disabled) + +RealmsStateUpdateDelay = 20 + +# +# WrongPass.MaxCount +# Description: Number of login attemps with wrong password before the account or IP will be +# banned. +# Default: 0 - (Disabled) +# 1+ - (Enabled) + +WrongPass.MaxCount = 0 + +# +# WrongPass.BanTime +# Description: Time (in seconds) for banning account or IP for invalid login attempts. +# Default: 600 - (10 minutes) +# 0 - (Permanent ban) + +WrongPass.BanTime = 600 + +# +# WrongPass.BanType +# Description: Ban type for invalid login attempts. +# Default: 0 - (Ban IP) +# 1 - (Ban Account) + +WrongPass.BanType = 0 + +# +# WrongPass.Logging +# Description: Additionally log attempted wrong password logging +# Default: 0 - (Disabled) +# 1 - (Enabled) + +WrongPass.Logging = 0 + +# +# BanExpiryCheckInterval +# Description: Time (in seconds) between checks for expired bans +# Default: 60 + +BanExpiryCheckInterval = 60 + +# +# SourceDirectory +# Description: The path to your TrinityCore source directory. +# If the path is left empty, the built-in CMAKE_SOURCE_DIR is used. +# Example: "../TrinityCore" +# Default: "" + +SourceDirectory = "" + +# +# MySQLExecutable +# Description: The path to your mysql cli binary. +# If the path is left empty, built-in path from cmake is used. +# Example: "C:/Program Files/MySQL/MySQL Server 5.6/bin/mysql.exe" +# "mysql.exe" +# "/usr/bin/mysql" +# Default: "" + +MySQLExecutable = "" + +# +# IPLocationFile +# Description: The path to your IP2Location database CSV file. +# Example: "C:/Trinity/IP2LOCATION-LITE-DB1.CSV" +# "/home/trinity/IP2LOCATION-LITE-DB1.CSV" +# Default: "" - (Disabled) + +IPLocationFile = "" + +# +################################################################################################### + +################################################################################################### +# MYSQL SETTINGS +# +# LoginDatabaseInfo +# Description: Database connection settings for the realm server. +# Example: "hostname;port;username;password;database" +# ".;somenumber;username;password;database" - (Use named pipes on Windows +# "enable-named-pipe" to [mysqld] +# section my.ini) +# ".;/path/to/unix_socket;username;password;database" - (use Unix sockets on +# Unix/Linux) +# Default: "127.0.0.1;3306;trinity;trinity;auth" + +LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth" + +# +# LoginDatabase.WorkerThreads +# Description: The amount of worker threads spawned to handle asynchronous (delayed) MySQL +# statements. Each worker thread is mirrored with its own connection to the +# MySQL server and their own thread on the MySQL server. +# Default: 1 + +LoginDatabase.WorkerThreads = 1 + +# +# LoginDatabase.SynchThreads +# Description: The amount of MySQL connections spawned to handle. +# Default: 1 - (LoginDatabase.WorkerThreads) + +LoginDatabase.SynchThreads = 1 + +# +################################################################################################### + +################################################################################################### +# UPDATE SETTINGS +# +# Updates.EnableDatabases +# Description: A mask that describes which databases shall be updated. +# +# Following flags are available +# DATABASE_LOGIN = 1, // Auth database +# +# Default: 0 - (All Disabled) +# 1 - (All Enabled) + +Updates.EnableDatabases = 0 + +# +# Updates.AutoSetup +# Description: Auto populate empty databases. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.AutoSetup = 1 + +# +# Updates.Redundancy +# Description: Perform data redundancy checks through hashing +# to detect changes on sql updates and reapply it. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.Redundancy = 1 + +# +# Updates.ArchivedRedundancy +# Description: Check hashes of archived updates (slows down startup). +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Updates.ArchivedRedundancy = 0 + +# +# Updates.AllowRehash +# Description: Inserts the current file hash in the database if it is left empty. +# Useful if you want to mark a file as applied but you don't know its hash. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.AllowRehash = 1 + +# +# Updates.CleanDeadRefMaxCount +# Description: Cleans dead/ orphaned references that occur if an update was removed or renamed and edited in one step. +# It only starts the clean up if the count of the missing updates is below or equal the Updates.CleanDeadRefMaxCount value. +# This way prevents erasing of the update history due to wrong source directory state (maybe wrong branch or bad revision). +# Disable this if you want to know if the database is in a possible "dirty state". +# Default: 3 - (Enabled) +# 0 - (Disabled) +# -1 - (Enabled - unlimited) + +Updates.CleanDeadRefMaxCount = 3 + +# +################################################################################################### + +################################################################################################### +# +# LOGGING SYSTEM SETTINGS +# +# Appender config values: Given a appender "name" +# Appender.name +# Description: Defines 'where to log' +# Format: Type,LogLevel,Flags,optional1,optional2,optional3 +# +# Type +# 0 - (None) +# 1 - (Console) +# 2 - (File) +# 3 - (DB) +# +# LogLevel +# 0 - (Disabled) +# 1 - (Trace) +# 2 - (Debug) +# 3 - (Info) +# 4 - (Warn) +# 5 - (Error) +# 6 - (Fatal) +# +# Flags: +# 0 - None +# 1 - Prefix Timestamp to the text +# 2 - Prefix Log Level to the text +# 4 - Prefix Log Filter type to the text +# 8 - Append timestamp to the log file name. Format: YYYY-MM-DD_HH-MM-SS (Only used with Type = 2) +# 16 - Make a backup of existing file before overwrite (Only used with Mode = w) +# +# Colors (read as optional1 if Type = Console) +# Format: "fatal error warn info debug trace" +# 0 - BLACK +# 1 - RED +# 2 - GREEN +# 3 - BROWN +# 4 - BLUE +# 5 - MAGENTA +# 6 - CYAN +# 7 - GREY +# 8 - YELLOW +# 9 - LRED +# 10 - LGREEN +# 11 - LBLUE +# 12 - LMAGENTA +# 13 - LCYAN +# 14 - WHITE +# Example: "13 11 9 5 3 1" +# +# File: Name of the file (read as optional1 if Type = File) +# Allows to use one "%s" to create dynamic files +# +# Mode: Mode to open the file (read as optional2 if Type = File) +# a - (Append) +# w - (Overwrite) +# +# MaxFileSize: Maximum file size of the log file before creating a new log file +# (read as optional3 if Type = File) +# Size is measured in bytes expressed in a 64-bit unsigned integer. +# Maximum value is 4294967295 (4 gb). Leave blank for no limit. +# NOTE: Does not work with dynamic filenames. +# Example: 536870912 (512 mb) +# + +Appender.Console=1,2,0 +Appender.Auth=2,2,0,Auth.log,w + +# Logger config values: Given a logger "name" +# Logger.name +# Description: Defines 'What to log' +# Format: LogLevel,AppenderList +# +# LogLevel +# 0 - (Disabled) +# 1 - (Trace) +# 2 - (Debug) +# 3 - (Info) +# 4 - (Warn) +# 5 - (Error) +# 6 - (Fatal) +# +# AppenderList: List of appenders linked to logger +# (Using spaces as separator). +# + +Logger.root=3,Console Auth + +# +################################################################################################### diff --git a/src/server/authserver/authserver.ico b/src/server/authserver/authserver.ico new file mode 100644 index 0000000000..da318f48a8 Binary files /dev/null and b/src/server/authserver/authserver.ico differ diff --git a/src/server/authserver/authserver.rc b/src/server/authserver/authserver.rc new file mode 100644 index 0000000000..a8270e06ea --- /dev/null +++ b/src/server/authserver/authserver.rc @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + * + * 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 . + */ + +#include "resource.h" +#include "revision_data.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" //"afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APPICON ICON "authserver.ico" + +///////////////////////////////////////////////////////////////////////////// +// Neutre (Par défaut système) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION + +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + +#ifndef _DEBUG + FILEFLAGS 0 +#else + #define VER_PRERELEASE VS_FF_PRERELEASE + #define VER_PRIVATEBUILD VS_FF_PRIVATEBUILD + #define VER_DEBUG 0 + FILEFLAGS (VER_PRIVATEBUILD|VER_PRERELEASE|VER_DEBUG) +#endif + +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080004b0" + BEGIN + VALUE "CompanyName", VER_COMPANYNAME_STR + VALUE "FileDescription", "TrinityCore Authentication Server Daemon" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "authserver" + VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR + VALUE "OriginalFilename", "authserver.exe" + VALUE "ProductName", "TrinityCore Authentication Server" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x800, 1200 + END +END +#endif diff --git a/src/server/authserver/resource.h b/src/server/authserver/resource.h new file mode 100644 index 0000000000..7dc5cb9ef7 --- /dev/null +++ b/src/server/authserver/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by TrinityCore.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/server/bnetserver/bnetserver.conf.dist b/src/server/bnetserver/bnetserver.conf.dist index ef8d4cb210..35dcb1dcb5 100644 --- a/src/server/bnetserver/bnetserver.conf.dist +++ b/src/server/bnetserver/bnetserver.conf.dist @@ -1,7 +1,7 @@ ############################################### # Trinity Core Auth Server configuration file # ############################################### -[bnetserver] +[authserver] ################################################################################################### # SECTION INDEX @@ -71,7 +71,7 @@ BindIP = "0.0.0.0" # # PidFile # Description: Auth server PID file. -# Example: "./bnetserver.pid" - (Enabled) +# Example: "./authserver.pid" - (Enabled) # Default: "" - (Disabled) PidFile = "" diff --git a/src/server/scripts/Commands/cs_battlenet_account.cpp b/src/server/scripts/Commands/cs_battlenet_account.cpp index c76660fb7b..22819f2fa2 100644 --- a/src/server/scripts/Commands/cs_battlenet_account.cpp +++ b/src/server/scripts/Commands/cs_battlenet_account.cpp @@ -155,7 +155,7 @@ public: else { handler->PSendSysMessage("IP2Location] No information"); - TC_LOG_DEBUG("server.bnetserver", "IP2Location] No information"); + TC_LOG_DEBUG("server.authserver", "IP2Location] No information"); } } else if (param == "off") diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index abc0d677a6..a129217137 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -3965,7 +3965,7 @@ Logger.sql.updates=3,Console Server #Logger.rbac=3,Console Server #Logger.scripts=3,Console Server #Logger.scripts.ai=3,Console Server -#Logger.server.bnetserver=3,Console Server +#Logger.server.authserver=3,Console Server #Logger.spells=3,Console Server #Logger.spells.periodic=3,Console Server #Logger.sql.dev=3,Console Server