diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp index d068d916e..2c3506db0 100644 --- a/loginserver/account_management.cpp +++ b/loginserver/account_management.cpp @@ -3,6 +3,7 @@ #include "../common/event/task_scheduler.h" #include "../common/event/event_loop.h" #include "../common/net/dns.h" +#include "../common/string_util.h" extern LoginServer server; EQ::Event::TaskScheduler task_runner; @@ -377,16 +378,22 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( } ); - EQ::Net::DNSLookup( - "login.eqemulator.net", 5999, false, [&](const std::string &addr) { - if (addr.empty()) { - ret = 0; - running = false; - } + auto s = SplitString(server.options.GetEQEmuLoginServerAddress(), ':'); + if (s.size() == 2) { + auto address = s[0]; + auto port = std::stoi(s[1]); - mgr.Connect(addr, 5999); - } - ); + EQ::Net::DNSLookup( + address, port, false, [&](const std::string &addr) { + if (addr.empty()) { + ret = 0; + running = false; + } + + mgr.Connect(addr, port); + } + ); + } std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); @@ -407,3 +414,114 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( return res.get(); } + +uint32 AccountManagement::HealthCheckUserLogin() +{ + std::string in_account_username = "healthcheckuser"; + std::string in_account_password = "healthcheckpassword"; + + auto res = task_runner.Enqueue( + [&]() -> uint32 { + bool running = true; + uint32 ret = 0; + + EQ::Net::DaybreakConnectionManager mgr; + std::shared_ptr c; + + mgr.OnNewConnection( + [&](std::shared_ptr connection) { + c = connection; + } + ); + + mgr.OnConnectionStateChange( + [&]( + std::shared_ptr conn, + EQ::Net::DbProtocolStatus from, + EQ::Net::DbProtocolStatus to + ) { + if (EQ::Net::StatusConnected == to) { + EQ::Net::DynamicPacket p; + p.PutUInt16(0, 1); //OP_SessionReady + p.PutUInt32(2, 2); + c->QueuePacket(p); + } + else if (EQ::Net::StatusDisconnected == to) { + running = false; + } + } + ); + + mgr.OnPacketRecv( + [&](std::shared_ptr conn, const EQ::Net::Packet &p) { + auto opcode = p.GetUInt16(0); + switch (opcode) { + case 0x0017: //OP_ChatMessage + { + size_t buffer_len = + in_account_username.length() + in_account_password.length() + 2; + + std::unique_ptr buffer(new char[buffer_len]); + + strcpy(&buffer[0], in_account_username.c_str()); + strcpy(&buffer[in_account_username.length() + 1], in_account_password.c_str()); + + size_t encrypted_len = buffer_len; + + if (encrypted_len % 8 > 0) { + encrypted_len = ((encrypted_len / 8) + 1) * 8; + } + + EQ::Net::DynamicPacket p; + p.Resize(12 + encrypted_len); + p.PutUInt16(0, 2); //OP_Login + p.PutUInt32(2, 3); + + eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true); + c->QueuePacket(p); + break; + } + case 0x0018: { + auto encrypt_size = p.Length() - 12; + if (encrypt_size % 8 > 0) { + encrypt_size = (encrypt_size / 8) * 8; + } + + std::unique_ptr decrypted(new char[encrypt_size]); + + eqcrypt_block((char *) p.Data() + 12, encrypt_size, &decrypted[0], false); + + EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size); + auto response_error = sp.GetUInt16(1); + + { + // we only care to see the response code + ret = response_error; + running = false; + } + break; + } + } + } + ); + + mgr.Connect("127.0.0.1", 5999); + + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + auto &loop = EQ::EventLoop::Get(); + while (running) { + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(end - begin).count() > 2000) { + ret = 0; + running = false; + } + + loop.Process(); + } + + return ret; + } + ); + + return res.get(); +} diff --git a/loginserver/account_management.h b/loginserver/account_management.h index e342ec172..2c92af69d 100644 --- a/loginserver/account_management.h +++ b/loginserver/account_management.h @@ -89,6 +89,8 @@ public: uint32 in_account_id, const std::string &in_account_password_hash ); + + static uint32 HealthCheckUserLogin(); }; diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 6eb5fea13..e92d408dc 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -233,11 +233,16 @@ void Client::Handle_Login(const char *data, unsigned int size) user = components[1]; } + // health checks + if (ProcessHealthCheck(user)) { + DoFailedLogin(); + return; + } + LogInfo( - "Attempting password based login [{0}] login [{1}] user [{2}]", + "Attempting password based login [{0}] login [{1}]", user, - db_loginserver, - user + db_loginserver ); ParseAccountString(user, user, db_loginserver); @@ -376,6 +381,7 @@ void Client::AttemptLoginAccountCreation( return; } + uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials( user, pass @@ -766,3 +772,11 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p) m_login_connection->Close(); } } +bool Client::ProcessHealthCheck(std::string username) +{ + if (username == "healthcheckuser") { + return true; + } + + return false; +} diff --git a/loginserver/client.h b/loginserver/client.h index 67c2f8e1f..4089e3d29 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -208,6 +208,7 @@ private: void LoginSendSessionReady(); void LoginSendLogin(); void LoginProcessLoginResponse(const EQ::Net::Packet &p); + static bool ProcessHealthCheck(std::string username); }; #endif diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp index eed4e6058..152266902 100644 --- a/loginserver/loginserver_command_handler.cpp +++ b/loginserver/loginserver_command_handler.cpp @@ -38,6 +38,7 @@ namespace LoginserverCommandHandler { function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens; function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount; function_map["world-admin:update"] = &LoginserverCommandHandler::UpdateLoginserverWorldAdminAccountPassword; + function_map["health:check-login"] = &LoginserverCommandHandler::HealthCheckLogin; EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); } @@ -281,4 +282,24 @@ namespace LoginserverCommandHandler { cmd(3).str() ); } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Checks login health using a test user"; + + std::vector arguments = {}; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + LogInfo("[CLI] [HealthCheck] Response code [{}]", AccountManagement::HealthCheckUserLogin()); + } } diff --git a/loginserver/loginserver_command_handler.h b/loginserver/loginserver_command_handler.h index e47382022..fc2f2cbeb 100644 --- a/loginserver/loginserver_command_handler.h +++ b/loginserver/loginserver_command_handler.h @@ -14,6 +14,7 @@ namespace LoginserverCommandHandler { void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description); + void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description); }; diff --git a/loginserver/loginserver_webserver.cpp b/loginserver/loginserver_webserver.cpp index 8fc7780bf..bea7dc8ae 100644 --- a/loginserver/loginserver_webserver.cpp +++ b/loginserver/loginserver_webserver.cpp @@ -28,7 +28,7 @@ namespace LoginserverWebserver { } Json::Value response; - auto iter = server.server_manager->getWorldServers().begin(); + auto iter = server.server_manager->getWorldServers().begin(); while (iter != server.server_manager->getWorldServers().end()) { Json::Value row; row["server_long_name"] = (*iter)->GetServerLongName(); @@ -297,6 +297,24 @@ namespace LoginserverWebserver { LoginserverWebserver::SendResponse(response, res); } ); + + api.Get( + "/probes/healthcheck", [](const httplib::Request &request, httplib::Response &res) { + Json::Value response; + uint32 login_response = AccountManagement::HealthCheckUserLogin(); + + response["status"] = login_response; + if (login_response == 0) { + response["message"] = "Process unresponsive, exiting..."; + LogInfo("Probes healthcheck unresponsive, exiting..."); + } + + LoginserverWebserver::SendResponse(response, res); + if (login_response == 0) { + std::exit(0); + } + } + ); } /** diff --git a/loginserver/main.cpp b/loginserver/main.cpp index d505ffc5d..50f86d673 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -10,6 +10,7 @@ #include "login_server.h" #include "loginserver_webserver.h" #include "loginserver_command_handler.h" +#include "../common/string_util.h" #include #include #include @@ -20,6 +21,7 @@ LoginServer server; EQEmuLogSys LogSys; bool run_server = true; +void ResolveAddresses(); void CatchSignal(int sig_num) { } @@ -118,11 +120,13 @@ void start_web_server() httplib::Server api; - api.set_logger([](const auto& req, const auto& res) { - if (!req.path.empty()) { - LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port); + api.set_logger( + [](const auto &req, const auto &res) { + if (!req.path.empty()) { + LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port); + } } - }); + ); LoginserverWebserver::RegisterRoutes(api); api.listen("0.0.0.0", web_api_port);