From 16d620b84d614e255bfd09f17634fcc54d554960 Mon Sep 17 00:00:00 2001 From: Meltie2013 Date: Thu, 4 Feb 2021 16:02:02 -0600 Subject: [PATCH] Rewrite Soap Server (#130) Co-authored-by: Antz --- src/mangosd/SOAP/SoapThread.cpp | 284 +++++++++++--------------------- src/mangosd/SOAP/SoapThread.h | 50 ++++-- src/mangosd/mangosd.cpp | 17 +- 3 files changed, 132 insertions(+), 219 deletions(-) diff --git a/src/mangosd/SOAP/SoapThread.cpp b/src/mangosd/SOAP/SoapThread.cpp index dcf5d9ee..ea3921fa 100644 --- a/src/mangosd/SOAP/SoapThread.cpp +++ b/src/mangosd/SOAP/SoapThread.cpp @@ -22,221 +22,121 @@ * and lore are copyrighted by Blizzard Entertainment, Inc. */ -#include -#include - #include "SoapThread.h" -#include "soapH.h" + +#include "AccountMgr.h" +#include "Log.h" +#include "World.h" + #include "soapStub.h" -#include "World.h" -#include "Log.h" -#include "AccountMgr.h" - - -/// WARNING! This code needs serious reviewing - - -struct SOAPCommand +void SoapThread(const std::string& host, uint16 port) { - public: - void appendToPrintBuffer(const char* msg) - { - m_printBuffer += msg; - } + struct soap soap; + soap_init(&soap); + soap_set_imode(&soap, SOAP_C_UTFSTRING); + soap_set_omode(&soap, SOAP_C_UTFSTRING); - void setCommandSuccess(bool val) - { - m_success = val; - } + // check every 3 seconds if world ended + soap.accept_timeout = 3; + soap.recv_timeout = 5; + soap.send_timeout = 5; - bool hasCommandSucceeded() - { - return m_success; - } + if (!soap_valid_socket(soap_bind(&soap, host.c_str(), port, 100))) + { + sLog.outError("SoapThread: couldn't bind to %s:%d", host.c_str(), port); + exit(-1); + } - static void print(void* callbackArg, const char* msg) - { - ((SOAPCommand*)callbackArg)->appendToPrintBuffer(msg); - } + sLog.outString("SoapThread: Bound to http://%s:%d", host.c_str(), port); - static void commandFinished(void* callbackArg, bool success); + while (!World::IsStopped()) + { + if (!soap_valid_socket(soap_accept(&soap))) + { + continue; // ran into an accept timeout + } - bool m_success; - std::string m_printBuffer; -}; + sLog.outString("Accepted connection from IP %d.%d.%d.%d", (int)(soap.ip >> 24) & 0xFF, (int)(soap.ip >> 16) & 0xFF, (int)(soap.ip >> 8) & 0xFF, (int)soap.ip & 0xFF); + struct soap* thread_soap = soap_copy(&soap); // make a safe copy + process_message(thread_soap); + } - -class SoapPool : public ACE_Task -{ - public: - virtual int svc(void) override - { - while (1) - { - ACE_Message_Block* mb = 0; - if (this->getq(mb) == -1) - { - break; - } - - // Process the message. - process_message(mb); - } - return 0; - } - - private: - void process_message(ACE_Message_Block* mb) - { - struct soap* soap; - ACE_OS::memcpy(&soap, mb->rd_ptr(), sizeof(struct soap*)); - mb->release(); - - soap_serve(soap); - - soap_destroy(soap); // dealloc C++ data - soap_end(soap); // dealloc data and clean up - soap_done(soap); // detach soap struct - free(soap); - } -}; - - - -SoapThread::~SoapThread() -{ - if(pool_) - { - delete pool_; - } + soap_done(&soap); } -int SoapThread::open(void* unused) +void process_message(struct soap* soap_message) { - // create pool - pool_ = new SoapPool; - pool_->activate(THR_NEW_LWP | THR_JOINABLE, SOAP_THREADS); - - int m; - soap_init(&soap_); - soap_set_imode(&soap_, SOAP_C_UTFSTRING); - soap_set_omode(&soap_, SOAP_C_UTFSTRING); - m = soap_bind(&soap_, host_, port_, 100); - - if (m < 0) - { - sLog.outError("SoapThread: couldn't bind to %s:%d", host_, port_); - return -1; - } - - // check every 3 seconds if world ended - soap_.accept_timeout = 3; - soap_.recv_timeout = 5; - soap_.send_timeout = 5; - - activate(); - return 0; + soap_serve(soap_message); + soap_destroy(soap_message); // dealloc C++ data + soap_end(soap_message); // dealloc data and clean up + soap_done(soap_message); // detach soap struct + free(soap_message); } - -int SoapThread::svc() -{ - int s; - sLog.outString("SOAP Thread started (listening on %s:%d)", host_, port_); - while (!World::IsStopped()) - { - s = soap_accept(&soap_); - - if (s < 0) - { - // ran into an accept timeout - continue; - } - - struct soap* thread_soap = soap_copy(&soap_);// make a safe copy - - ACE_Message_Block* mb = new ACE_Message_Block(sizeof(struct soap*)); - ACE_OS::memcpy(mb->wr_ptr(), &thread_soap, sizeof(struct soap*)); - pool_->putq(mb); - } - - pool_->msg_queue()->deactivate(); - pool_->wait(); - - soap_done(&soap_); - sLog.outString("SOAP Thread stopped"); - return 0; -} - - -/* -Code used for generating stubs: - -int ns1__executeCommand(char* command, char** result); -*/ - int ns1__executeCommand(soap* soap, char* command, char** result) { - // security check - if (!soap->userid || !soap->passwd) - { - DEBUG_LOG("MaNGOSsoap: Client didn't provide login information"); - return 401; - } + // security check + if (!soap->userid || !soap->passwd) + { + sLog.outString("SoapThread: Client didn't provide login information"); + return 401; + } - uint32 accountId = sAccountMgr.GetId(soap->userid); - if (!accountId) - { - DEBUG_LOG("MaNGOSsoap: Client used invalid username '%s'", soap->userid); - return 401; - } + uint32 accountId = sAccountMgr.GetId(soap->userid); + if (!accountId) + { + sLog.outString("SoapThread: Client used invalid username %s", soap->userid); + return 401; + } - if (!sAccountMgr.CheckPassword(accountId, soap->passwd)) - { - DEBUG_LOG("MaNGOSsoap: invalid password for account '%s'", soap->userid); - return 401; - } + if (!sAccountMgr.CheckPassword(accountId, soap->passwd)) + { + sLog.outString("SoapThread: Client sent an invalid password for account %s", soap->passwd); + return 401; + } - if (sAccountMgr.GetSecurity(accountId) < SEC_ADMINISTRATOR) - { - DEBUG_LOG("MaNGOSsoap: %s's gmlevel is too low", soap->userid); - return 403; - } + /* ToDo: Add realm check */ + if (sAccountMgr.GetSecurity(accountId) < SEC_ADMINISTRATOR) + { + sLog.outString("SoapThread: %s's account security level is to low", soap->userid); + return 403; + } - if (!command || !*command) - { - return soap_sender_fault(soap, "Command mustn't be empty", "The supplied command was an empty string"); - } + if (!command || !*command) + { + return soap_sender_fault(soap, "Command can not be empty", "The supplied command was an empty string"); + } - DEBUG_LOG("MaNGOSsoap: got command '%s'", command); - SOAPCommand connection; + sLog.outString("SoapThread: Recieved command %s", command); + SOAPCommand connection; - // commands are executed in the world thread. We have to wait for them to be completed - { - // CliCommandHolder will be deleted from world, accessing after queueing is NOT save - CliCommandHolder* cmd = new CliCommandHolder(accountId, SEC_CONSOLE, &connection, command, &SOAPCommand::print, &SOAPCommand::commandFinished); - sWorld.QueueCliCommand(cmd); - } + // commands are executed in the world thread and have to wait till they are completed. + { + CliCommandHolder* cmd = new CliCommandHolder(accountId, SEC_CONSOLE, &connection, command, &SOAPCommand::print, &SOAPCommand::commandFinished); + sWorld.QueueCliCommand(cmd); + } - ACE_OS::sleep(1); + // wait until the command has finished executing + connection.finishedPromise.get_future().wait(); - char* printBuffer = soap_strdup(soap, connection.m_printBuffer.c_str()); - if (connection.hasCommandSucceeded()) - { - *result = printBuffer; - return SOAP_OK; - } - else - { - return soap_sender_fault(soap, printBuffer, printBuffer); - } + // the command has finished executing already + char* printBuffer = soap_strdup(soap, connection.m_printBuffer.c_str()); + if (connection.hasCommandSucceeded()) + { + *result = printBuffer; + return SOAP_OK; + } + else + { + return soap_sender_fault(soap, printBuffer, printBuffer); + } } void SOAPCommand::commandFinished(void* soapconnection, bool success) { - SOAPCommand* con = (SOAPCommand*)soapconnection; - con->setCommandSuccess(success); + SOAPCommand* con = (SOAPCommand*)soapconnection; + con->setCommandSuccess(success); } //////////////////////////////////////////////////////////////////////////////// @@ -247,10 +147,10 @@ void SOAPCommand::commandFinished(void* soapconnection, bool success) struct Namespace namespaces[] = { - { "SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/" }, // must be first - { "SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/" }, // must be second - { "xsi", "http://www.w3.org/1999/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance" }, - { "xsd", "http://www.w3.org/1999/XMLSchema", "http://www.w3.org/*/XMLSchema" }, - { "ns1", "urn:MaNGOS" }, // "ns1" namespace prefix - { NULL, NULL } + { "SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/" }, // must be first + { "SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/" }, // must be second + { "xsi", "http://www.w3.org/1999/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance" }, + { "xsd", "http://www.w3.org/1999/XMLSchema", "http://www.w3.org/*/XMLSchema" }, + { "ns1", "urn:MaNGOS" }, // "ns1" namespace prefix + { NULL, NULL } }; diff --git a/src/mangosd/SOAP/SoapThread.h b/src/mangosd/SOAP/SoapThread.h index a66d6182..bab2c756 100644 --- a/src/mangosd/SOAP/SoapThread.h +++ b/src/mangosd/SOAP/SoapThread.h @@ -25,29 +25,47 @@ #ifndef MANGOS_H_SOAPTHREAD #define MANGOS_H_SOAPTHREAD -#include - #include "Common.h" -#include "soapH.h" -class SoapPool; +#include +#include +#include -class SoapThread: public ACE_Task_Base +void process_message(struct soap* soap_message); +void SoapThread(const std::string& host, uint16 port); + +class SOAPCommand { - enum { SOAP_THREADS = 1 }; //TODO: configure in mangosd.conf +public: + SOAPCommand() : m_success(false) { } + ~SOAPCommand() { } - public: - SoapThread(uint16 port, const char* host) : host_(host), port_(port), pool_(NULL) {} - virtual ~SoapThread(); + void appendToPrintBuffer(std::string msg) + { + m_printBuffer += msg; + } - virtual int svc() override; - virtual int open(void*) override; + void setCommandSuccess(bool val) + { + m_success = val; + finishedPromise.set_value(); + } - private: - const char *host_; - uint16 port_; - SoapPool *pool_; - struct soap soap_; + bool hasCommandSucceeded() const + { + return m_success; + } + + static void print(void* callbackArg, const char* msg) + { + ((SOAPCommand*)callbackArg)->appendToPrintBuffer(msg); + } + + static void commandFinished(void* callbackArg, bool success); + + bool m_success; + std::string m_printBuffer; + std::promise finishedPromise; }; #endif diff --git a/src/mangosd/mangosd.cpp b/src/mangosd/mangosd.cpp index c4054fbc..ce7daef0 100644 --- a/src/mangosd/mangosd.cpp +++ b/src/mangosd/mangosd.cpp @@ -478,13 +478,14 @@ int main(int argc, char** argv) // 3. Start the SOAP listener thread, if enabled //************************************************************************************************************************ #ifdef ENABLE_SOAP - SoapThread* soapThread = NULL; + std::shared_ptr soapThread; if (sConfig.GetBoolDefault("SOAP.Enabled", false)) { - host = sConfig.GetStringDefault("SOAP.IP", "127.0.0.1"); - port = sConfig.GetIntDefault("SOAP.Port", 7878); - soapThread = new SoapThread(port, host.c_str()); - soapThread->open(0); + soapThread.reset(new std::thread(SoapThread, sConfig.GetStringDefault("SOAP.IP", "127.0.0.1"), uint16(sConfig.GetIntDefault("SOAP.Port", 7878))), [](std::thread* thread) + { + thread->join(); + delete thread; + }); } #else /* ENABLE_SOAP */ if (sConfig.GetBoolDefault("SOAP.Enabled", false)) @@ -533,12 +534,6 @@ int main(int argc, char** argv) delete freezeThread; } -#ifdef ENABLE_SOAP - if (soapThread) - { - delete soapThread; - } -#endif if (raThread) { delete raThread;