From 6a64d845c2cee9acddd511f5f46c9d0a8250292a Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 8 Aug 2019 18:45:10 -0700 Subject: [PATCH] Async eqemu login credential lookup --- common/event/task_scheduler.h | 114 ++++++++++++++++++++ loginserver/account_management.cpp | 101 +++++++++++++++++ loginserver/account_management.h | 10 ++ loginserver/loginserver_command_handler.cpp | 35 +++++- loginserver/loginserver_command_handler.h | 1 + loginserver/loginserver_webserver.cpp | 32 +++++- 6 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 common/event/task_scheduler.h diff --git a/common/event/task_scheduler.h b/common/event/task_scheduler.h new file mode 100644 index 000000000..6cfe15f00 --- /dev/null +++ b/common/event/task_scheduler.h @@ -0,0 +1,114 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace EQ +{ + namespace Event + { + class TaskScheduler + { + public: + static const int DefaultThreadCount = 4; + + TaskScheduler() : _running(false) + { + Start(DefaultThreadCount); + } + + TaskScheduler(size_t threads) : _running(false) + { + Start(threads); + } + + ~TaskScheduler() { + Stop(); + } + + void Start(size_t threads) { + if (true == _running) { + return; + } + + _running = true; + + for (size_t i = 0; i < threads; ++i) { + _threads.push_back(std::thread(std::bind(&TaskScheduler::ProcessWork, this))); + } + } + + void Stop() { + if (false == _running) { + return; + } + + { + std::unique_lock lock(_lock); + _running = false; + } + + _cv.notify_all(); + + for (auto &t : _threads) { + t.join(); + } + } + + template + auto Enqueue(Fn&& fn, Args&&... args) -> std::future::type> { + using return_type = typename std::result_of::type; + + auto task = std::make_shared>( + std::bind(std::forward(fn), std::forward(args)...) + ); + + std::future res = task->get_future(); + { + std::unique_lock lock(_lock); + + if (false == _running) { + throw std::runtime_error("Enqueue on stopped scheduler."); + } + + _tasks.emplace([task]() { (*task)(); }); + } + + _cv.notify_one(); + return res; + } + + private: + void ProcessWork() { + for (;;) { + std::function work; + + { + std::unique_lock lock(_lock); + _cv.wait(lock, [this] { return !_running || !_tasks.empty(); }); + + if (false == _running) { + return; + } + + work = std::move(_tasks.front()); + _tasks.pop(); + } + + work(); + } + + } + + bool _running = true; + std::vector _threads; + std::mutex _lock; + std::condition_variable _cv; + std::queue> _tasks; + }; + } +} diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp index 2817b09ff..139fe9690 100644 --- a/loginserver/account_management.cpp +++ b/loginserver/account_management.cpp @@ -20,8 +20,12 @@ #include "account_management.h" #include "login_server.h" +#include "../common/event/task_scheduler.h" +#include "../common/event/event_loop.h" +#include "../common/net/dns.h" extern LoginServer server; +EQ::Event::TaskScheduler task_runner; /** * @param username @@ -238,3 +242,100 @@ bool AccountManagement::UpdateLoginserverUserCredentials( return true; } + +bool AccountManagement::CheckExternalLoginserverUserCredentials(const std::string &in_account_username, const std::string &in_account_password) +{ + auto res = task_runner.Enqueue([&]() -> bool { + bool running = true; + bool ret = false; + 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); + + if (response_error > 101) { + ret = false; + running = false; + } + else { + ret = true; + running = false; + } + break; + } + } + }); + + EQ::Net::DNSLookup("login.eqemulator.net", 5999, false, [&](const std::string &addr) { + if (addr == "") { + ret = false; + running = false; + } + + mgr.Connect(addr, 5999); + }); + + auto &loop = EQ::EventLoop::Get(); + while (true == running) { + loop.Process(); + } + + return ret; + }); + + return res.get(); +} diff --git a/loginserver/account_management.h b/loginserver/account_management.h index 3ab179405..680122213 100644 --- a/loginserver/account_management.h +++ b/loginserver/account_management.h @@ -70,6 +70,16 @@ public: const std::string &in_account_password, const std::string &source_loginserver = "local" ); + + /** + * @param in_account_username + * @param in_account_password + * @return + */ + static bool CheckExternalLoginserverUserCredentials( + const std::string &in_account_username, + const std::string &in_account_password + ); }; diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp index 68cd3ea5d..81647d60d 100644 --- a/loginserver/loginserver_command_handler.cpp +++ b/loginserver/loginserver_command_handler.cpp @@ -51,6 +51,7 @@ namespace LoginserverCommandHandler { * Register commands */ function_map["login-user:check-credentials"] = &LoginserverCommandHandler::CheckLoginserverUserCredentials; + function_map["login-user:check-external-credentials"] = &LoginserverCommandHandler::CheckExternalLoginserverUserCredentials; function_map["login-user:create"] = &LoginserverCommandHandler::CreateLocalLoginserverAccount; function_map["login-user:update-credentials"] = &LoginserverCommandHandler::UpdateLoginserverUserCredentials; function_map["web-api-token:create"] = &LoginserverCommandHandler::CreateLoginserverApiToken; @@ -203,10 +204,12 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - AccountManagement::CheckLoginserverUserCredentials( + auto res = AccountManagement::CheckLoginserverUserCredentials( cmd("--username").str(), cmd("--password").str() ); + + LogInfo("Credentials were {0}", res == true ? "accepted" : "not accepted"); } /** @@ -236,4 +239,34 @@ namespace LoginserverCommandHandler { cmd("--password").str() ); } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Check user external login credentials"; + + std::vector arguments = { + "--username", + "--password" + }; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + auto res = AccountManagement::CheckExternalLoginserverUserCredentials( + cmd("--username").str(), + cmd("--password").str() + ); + + LogInfo("Credentials were {0}", res == true ? "accepted" : "not accepted"); + } } diff --git a/loginserver/loginserver_command_handler.h b/loginserver/loginserver_command_handler.h index d155c8a0c..c7abc50a6 100644 --- a/loginserver/loginserver_command_handler.h +++ b/loginserver/loginserver_command_handler.h @@ -32,6 +32,7 @@ namespace LoginserverCommandHandler { void CreateLoginserverWorldAdminAccount(int argc, char **argv, argh::parser &cmd, std::string &description); void CheckLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); + void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); }; diff --git a/loginserver/loginserver_webserver.cpp b/loginserver/loginserver_webserver.cpp index 07d680fa1..a26115075 100644 --- a/loginserver/loginserver_webserver.cpp +++ b/loginserver/loginserver_webserver.cpp @@ -158,6 +158,36 @@ namespace LoginserverWebserver { LoginserverWebserver::SendResponse(response, res); } ); + + api.Post( + "/account/credentials/validate/external", [](const httplib::Request &request, httplib::Response &res) { + LoginserverWebserver::TokenManager::AuthCanRead(request, res); + Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); + std::string username = request_body.get("username", "").asString(); + std::string password = request_body.get("password", "").asString(); + + Json::Value response; + if (username.empty() || password.empty()) { + response["error"] = "Username or password not set"; + LoginserverWebserver::SendResponse(response, res); + return; + } + + bool credentials_valid = AccountManagement::CheckExternalLoginserverUserCredentials( + username, + password + ); + + if (credentials_valid) { + response["message"] = "Credentials valid!"; + } + else { + response["error"] = "Credentials invalid!"; + } + + LoginserverWebserver::SendResponse(response, res); + } + ); } /** @@ -351,4 +381,4 @@ namespace LoginserverWebserver { return {}; } -} \ No newline at end of file +}