mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-13 17:19:07 +00:00
427 lines
10 KiB
C++
427 lines
10 KiB
C++
/*
|
|
* Copyright (C)
|
|
* Copyright (C)
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/** \file
|
|
\ingroup Trinityd
|
|
*/
|
|
|
|
#include "Common.h"
|
|
#include "Configuration/Config.h"
|
|
#include "Database/DatabaseEnv.h"
|
|
#include "AccountMgr.h"
|
|
#include "Log.h"
|
|
#include "RASocket.h"
|
|
#include "Util.h"
|
|
#include "World.h"
|
|
#include "SHA1.h"
|
|
|
|
RASocket::RASocket()
|
|
{
|
|
_minLevel = uint8(sConfigMgr->GetIntDefault("RA.MinLevel", 3));
|
|
_commandExecuting = false;
|
|
}
|
|
|
|
int RASocket::open(void *)
|
|
{
|
|
ACE_INET_Addr remoteAddress;
|
|
|
|
if (peer().get_remote_addr(remoteAddress) == -1)
|
|
{
|
|
sLog->outError("RASocket::open: peer().get_remote_addr error is %s", ACE_OS::strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
sLog->outRemote("Incoming connection from %s", remoteAddress.get_host_addr());
|
|
|
|
return activate();
|
|
}
|
|
|
|
int RASocket::handle_close(ACE_HANDLE /*handle*/, ACE_Reactor_Mask /*mask*/)
|
|
{
|
|
sLog->outRemote("Closing connection");
|
|
peer().close_reader();
|
|
wait();
|
|
// While the above wait() will wait for the ::svc() to finish, it will not wait for the async event
|
|
// RASocket::commandfinished to be completed. Calling destroy() before the latter function ends
|
|
// will lead to using a freed pointer -> crash.
|
|
while (_commandExecuting.value())
|
|
ACE_OS::sleep(1);
|
|
|
|
destroy();
|
|
return 0;
|
|
}
|
|
|
|
int RASocket::send(const std::string& line)
|
|
{
|
|
#ifdef MSG_NOSIGNAL
|
|
ssize_t n = peer().send(line.c_str(), line.length(), MSG_NOSIGNAL);
|
|
#else
|
|
ssize_t n = peer().send(line.c_str(), line.length());
|
|
#endif // MSG_NOSIGNAL
|
|
|
|
return n == ssize_t(line.length()) ? 0 : -1;
|
|
}
|
|
|
|
int RASocket::recv_line(ACE_Message_Block& buffer)
|
|
{
|
|
char byte;
|
|
for (;;)
|
|
{
|
|
ssize_t n = peer().recv(&byte, sizeof(byte));
|
|
|
|
if (n < 0)
|
|
return -1;
|
|
|
|
if (n == 0)
|
|
{
|
|
// EOF, connection was closed
|
|
errno = ECONNRESET;
|
|
return -1;
|
|
}
|
|
|
|
ACE_ASSERT(n == sizeof(byte));
|
|
|
|
if (byte == '\n')
|
|
break;
|
|
else if (byte == '\r') /* Ignore CR */
|
|
continue;
|
|
else if (buffer.copy(&byte, sizeof(byte)) == -1)
|
|
return -1;
|
|
}
|
|
|
|
const char nullTerm = '\0';
|
|
if (buffer.copy(&nullTerm, sizeof(nullTerm)) == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RASocket::recv_line(std::string& out_line)
|
|
{
|
|
char buf[4096];
|
|
|
|
ACE_Data_Block db(sizeof (buf),
|
|
ACE_Message_Block::MB_DATA,
|
|
buf,
|
|
0,
|
|
0,
|
|
ACE_Message_Block::DONT_DELETE,
|
|
0);
|
|
|
|
ACE_Message_Block message_block(&db,
|
|
ACE_Message_Block::DONT_DELETE,
|
|
0);
|
|
|
|
if (recv_line(message_block) == -1)
|
|
{
|
|
sLog->outRemote("Recv error %s", ACE_OS::strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
out_line = message_block.rd_ptr();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RASocket::process_command(const std::string& command)
|
|
{
|
|
if (command.length() == 0)
|
|
return 0;
|
|
|
|
sLog->outRemote("Got command: %s", command.c_str());
|
|
|
|
// handle quit, exit and logout commands to terminate connection
|
|
if (command == "quit" || command == "exit" || command == "logout") {
|
|
(void) send("Bye\r\n");
|
|
return -1;
|
|
}
|
|
|
|
_commandExecuting = true;
|
|
CliCommandHolder* cmd = new CliCommandHolder(this, command.c_str(), &RASocket::zprint, &RASocket::commandFinished);
|
|
sWorld->QueueCliCommand(cmd);
|
|
|
|
// wait for result
|
|
ACE_Message_Block* mb;
|
|
for (;;)
|
|
{
|
|
if (getq(mb) == -1)
|
|
return -1;
|
|
|
|
if (mb->msg_type() == ACE_Message_Block::MB_BREAK)
|
|
{
|
|
mb->release();
|
|
break;
|
|
}
|
|
|
|
if (send(std::string(mb->rd_ptr(), mb->length())) == -1)
|
|
{
|
|
mb->release();
|
|
return -1;
|
|
}
|
|
|
|
mb->release();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RASocket::check_access_level(const std::string& user)
|
|
{
|
|
std::string safeUser = user;
|
|
|
|
AccountMgr::normalizeString(safeUser);
|
|
|
|
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS);
|
|
stmt->setString(0, safeUser);
|
|
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
|
|
|
if (!result)
|
|
{
|
|
sLog->outRemote("User %s does not exist in database", user.c_str());
|
|
return -1;
|
|
}
|
|
|
|
Field* fields = result->Fetch();
|
|
|
|
if (fields[1].GetUInt8() < _minLevel)
|
|
{
|
|
sLog->outRemote("User %s has no privilege to login", user.c_str());
|
|
return -1;
|
|
}
|
|
else if (fields[2].GetInt32() != -1)
|
|
{
|
|
sLog->outRemote("User %s has to be assigned on all realms (with RealmID = '-1')", user.c_str());
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RASocket::check_password(const std::string& user, const std::string& pass)
|
|
{
|
|
std::string safe_user = user;
|
|
AccountMgr::normalizeString(safe_user);
|
|
|
|
std::string safe_pass = pass;
|
|
AccountMgr::normalizeString(safe_pass);
|
|
|
|
std::string hash = AccountMgr::CalculateShaPassHash(safe_user, safe_pass);
|
|
|
|
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME);
|
|
|
|
stmt->setString(0, safe_user);
|
|
stmt->setString(1, hash);
|
|
|
|
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
|
|
|
if (!result)
|
|
{
|
|
sLog->outRemote("Wrong password for user: %s", user.c_str());
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RASocket::authenticate()
|
|
{
|
|
if (send(std::string("Username: ")) == -1)
|
|
return -1;
|
|
|
|
std::string user;
|
|
if (recv_line(user) == -1)
|
|
return -1;
|
|
|
|
if (send(std::string("Password: ")) == -1)
|
|
return -1;
|
|
|
|
std::string pass;
|
|
if (recv_line(pass) == -1)
|
|
return -1;
|
|
|
|
sLog->outRemote("Login attempt for user: %s", user.c_str());
|
|
|
|
if (check_access_level(user) == -1)
|
|
return -1;
|
|
|
|
if (check_password(user, pass) == -1)
|
|
return -1;
|
|
|
|
sLog->outRemote("User login: %s", user.c_str());
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int RASocket::subnegotiate()
|
|
{
|
|
char buf[1024];
|
|
|
|
ACE_Data_Block db(sizeof (buf),
|
|
ACE_Message_Block::MB_DATA,
|
|
buf,
|
|
0,
|
|
0,
|
|
ACE_Message_Block::DONT_DELETE,
|
|
0);
|
|
|
|
ACE_Message_Block message_block(&db,
|
|
ACE_Message_Block::DONT_DELETE,
|
|
0);
|
|
|
|
const size_t recv_size = message_block.space();
|
|
|
|
// Wait a maximum of 1000ms for negotiation packet - not all telnet clients may send it
|
|
ACE_Time_Value waitTime = ACE_Time_Value(1);
|
|
const ssize_t n = peer().recv(message_block.wr_ptr(),
|
|
recv_size, &waitTime);
|
|
|
|
if (n <= 0)
|
|
return int(n);
|
|
|
|
if (n >= 1024)
|
|
{
|
|
sLog->outRemote("RASocket::subnegotiate: allocated buffer 1024 bytes was too small for negotiation packet, size: %u", uint32(n));
|
|
return -1;
|
|
}
|
|
|
|
buf[n] = '\0';
|
|
|
|
#ifdef _DEBUG
|
|
for (uint8 i = 0; i < n; )
|
|
{
|
|
uint8 iac = buf[i];
|
|
if (iac == 0xFF) // "Interpret as Command" (IAC)
|
|
{
|
|
uint8 command = buf[++i];
|
|
std::stringstream ss;
|
|
switch (command)
|
|
{
|
|
case 0xFB: // WILL
|
|
ss << "WILL ";
|
|
break;
|
|
case 0xFC: // WON'T
|
|
ss << "WON'T ";
|
|
break;
|
|
case 0xFD: // DO
|
|
ss << "DO ";
|
|
break;
|
|
case 0xFE: // DON'T
|
|
ss << "DON'T ";
|
|
break;
|
|
default:
|
|
return -1; // not allowed
|
|
}
|
|
|
|
uint8 param = buf[++i];
|
|
ss << uint32(param);
|
|
sLog->outRemote(ss.str().c_str());
|
|
}
|
|
++i;
|
|
}
|
|
#endif
|
|
|
|
//! Just send back end of subnegotiation packet
|
|
uint8 const reply[2] = {0xFF, 0xF0};
|
|
|
|
#ifdef MSG_NOSIGNAL
|
|
return int(peer().send(reply, 2, MSG_NOSIGNAL));
|
|
#else
|
|
return int(peer().send(reply, 2));
|
|
#endif // MSG_NOSIGNAL
|
|
}
|
|
|
|
int RASocket::svc(void)
|
|
{
|
|
//! Subnegotiation may differ per client - do not react on it
|
|
subnegotiate();
|
|
|
|
if (send("Authentication required\r\n") == -1)
|
|
return -1;
|
|
|
|
if (authenticate() == -1)
|
|
{
|
|
(void) send("Authentication failed\r\n");
|
|
return -1;
|
|
}
|
|
|
|
// send motd
|
|
if (send(std::string(sWorld->GetMotd()) + "\r\n") == -1)
|
|
return -1;
|
|
|
|
for (;;)
|
|
{
|
|
// show prompt
|
|
if (send("TC> ") == -1)
|
|
return -1;
|
|
|
|
std::string line;
|
|
|
|
if (recv_line(line) == -1)
|
|
return -1;
|
|
|
|
if (process_command(line) == -1)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void RASocket::zprint(void* callbackArg, const char * szText)
|
|
{
|
|
if (!szText || !callbackArg)
|
|
return;
|
|
|
|
RASocket* socket = static_cast<RASocket*>(callbackArg);
|
|
size_t sz = strlen(szText);
|
|
|
|
ACE_Message_Block* mb = new ACE_Message_Block(sz);
|
|
mb->copy(szText, sz);
|
|
|
|
ACE_Time_Value tv = ACE_Time_Value::zero;
|
|
if (socket->putq(mb, &tv) == -1)
|
|
{
|
|
sLog->outRemote("Failed to enqueue message, queue is full or closed. Error is %s", ACE_OS::strerror(errno));
|
|
mb->release();
|
|
}
|
|
}
|
|
|
|
void RASocket::commandFinished(void* callbackArg, bool /*success*/)
|
|
{
|
|
if (!callbackArg)
|
|
return;
|
|
|
|
RASocket* socket = static_cast<RASocket*>(callbackArg);
|
|
|
|
ACE_Message_Block* mb = new ACE_Message_Block();
|
|
|
|
mb->msg_type(ACE_Message_Block::MB_BREAK);
|
|
|
|
// the message is 0 size control message to tell that command output is finished
|
|
// hence we don't put timeout, because it shouldn't increase queue size and shouldn't block
|
|
if (socket->putq(mb->duplicate()) == -1)
|
|
{
|
|
// getting here is bad, command can't be marked as complete
|
|
//sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Failed to enqueue command end message. Error is %s", ACE_OS::strerror(errno));
|
|
}
|
|
|
|
mb->release();
|
|
|
|
socket->_commandExecuting = false;
|
|
}
|