Async eqemu login credential lookup

This commit is contained in:
KimLS 2019-08-08 18:45:10 -07:00
parent 880de837d9
commit 6a64d845c2
6 changed files with 291 additions and 2 deletions

View File

@ -0,0 +1,114 @@
#pragma once
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <queue>
#include <future>
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<std::mutex> lock(_lock);
_running = false;
}
_cv.notify_all();
for (auto &t : _threads) {
t.join();
}
}
template<typename Fn, typename... Args>
auto Enqueue(Fn&& fn, Args&&... args) -> std::future<typename std::result_of<Fn(Args...)>::type> {
using return_type = typename std::result_of<Fn(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> 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<void()> work;
{
std::unique_lock<std::mutex> 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<std::thread> _threads;
std::mutex _lock;
std::condition_variable _cv;
std::queue<std::function<void()>> _tasks;
};
}
}

View File

@ -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<EQ::Net::DaybreakConnection> c;
mgr.OnNewConnection([&](std::shared_ptr<EQ::Net::DaybreakConnection> connection) {
c = connection;
});
mgr.OnConnectionStateChange([&](std::shared_ptr<EQ::Net::DaybreakConnection> 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<EQ::Net::DaybreakConnection> 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<char[]> 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<char[]> 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();
}

View File

@ -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
);
};

View File

@ -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<std::string> arguments = {
"--username",
"--password"
};
std::vector<std::string> 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");
}
}

View File

@ -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);
};

View File

@ -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 {};
}
}
}