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