mirror of
https://github.com/araxiaonline/TrinityCore2.git
synced 2026-06-18 13:59:39 -04:00
55ce180f28
- Logging System is asyncronous to improve performance.
- Each msg and Logger has a Log Type and Log Level assigned. Each msg is assigned the Logger of same Log Type or "root" Logger is selected if there is no Logger configured for the given Log Type
- Loggers have a list of Appenders to send the msg to. The Msg in the Logger is not sent to Appenders if the msg LogLevel is lower than Logger LogLevel.
- There are three (at the moment) types of Appenders: Console, File or DB (this is WIP, not working ATM). Msg is not written to the resource if msg LogLevel is lower than Appender LogLevel.
- Appender and Console Log levels can be changed while server is active with command '.set loglevel (a/l) name level'
Explanation of use with Sample config:
Appender.Console.Type=1 (1 = Console)
Appender.Console.Level=2 (2 = Debug)
Appender.Server.Type=2 (2 = File)
Appender.Server.Level=3 (3 = Info)
Appender.Server.File=Server.log
Appender.SQL.Type=2 (2 = File)
Appender.SQL.Level=1 (1 = Trace)
Appender.SQL.File=sql.log
Appenders=Console Server (NOTE: SQL has not been included here... that will make core ignore the config for "SQL" as it's not in this list)
Logger.root.Type=0 (0 = Default - if it's not created by config, server will create it with LogLevel = DISABLED)
Logger.root.Level=5 (5 = Error)
Logger.root.Appenders=Console
Logger.SQL.Type=26 (26 = SQL)
Logger.SQL.Level=3 (2 = Debug)
Logger.SQL.Appenders=Console Server SQL
Logger.SomeRandomName.Type=24 (24 = Guild)
Logger.SomeRandomName.Level=5 (5 = Error)
Loggers=root SQL SomeRandomName
* At loading Appender SQL will be ignored, as it's not present on "Appenders"
* sLog->outDebug(LOG_FILTER_GUILD, "Some log msg related to Guilds")
- Msg is sent to Logger of Type LOG_FILTER_GUILD (24). Logger with name SomeRandomName is found but it's LogLevel = 5 and Msg LogLevel=2... Msg is not logged
* sLog->outError(LOG_FILTER_GUILD, "Some error log msg related to Guilds")
- Msg is sent to Logger of Type LOG_FILTER_GUILD (24). Logger with name SomeRandomeName is found with proper LogLevel but Logger does not have any Appenders assigned to that logger... Msg is not logged
* sLog->outDebug(LOG_FILTER_SQL, "Some msg related to SQLs")
- Msg is sent to Logger SQL (matches type), as it matches LogLevel the msg is sent to Appenders Console, Server and SQL
- Appender Console has lower Log Level: Msg is logged to Console
- Appender Server has higher Log Level: Msg is not logged to file
- Appender SQL has lower Log Level: Msg is logged to file sql.log
* sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Some msg related to Battelgrounds")
- Msg is sent to Logger root (Type 0) as no Logger was found with Type LOG_FILTER_BATTLEGROUND (13). As Logger has higher LogLevel msg is not sent to any appender
* sLog->outError(LOG_FILTER_BATTLEGROUND, "Some error msg related to Battelgrounds")
- Msg is sent to Logger root (Type 0) as no Logger was found with Type LOG_FILTER_BATTLEGROUND (13). Msg has lower LogLevel and is sent to Appender Console
- Appender Console has lower LogLevel: Msg is logged to Console
413 lines
10 KiB
C++
Executable File
413 lines
10 KiB
C++
Executable File
/*
|
|
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* 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 = ConfigMgr::GetIntDefault("RA.MinLevel", 3);
|
|
}
|
|
|
|
RASocket::~RASocket()
|
|
{
|
|
}
|
|
|
|
int RASocket::open(void *)
|
|
{
|
|
ACE_INET_Addr remote_addr;
|
|
|
|
if (peer().get_remote_addr(remote_addr) == -1)
|
|
{
|
|
sLog->outError(LOG_FILTER_WORLDSERVER, "RASocket::open: peer().get_remote_addr error is %s", ACE_OS::strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Incoming connection from %s", remote_addr.get_host_addr());
|
|
|
|
return activate();
|
|
}
|
|
|
|
int RASocket::handle_close(ACE_HANDLE, ACE_Reactor_Mask)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Closing connection");
|
|
peer().close_reader();
|
|
wait();
|
|
destroy();
|
|
return 0;
|
|
}
|
|
|
|
int RASocket::send(const std::string& line)
|
|
{
|
|
return size_t(peer().send(line.c_str(), line.length())) == 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 null_term = '\0';
|
|
if (buffer.copy(&null_term, sizeof(null_term)) == -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->outDebug(LOG_FILTER_REMOTECOMMAND, "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->outDebug(LOG_FILTER_REMOTECOMMAND, "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;
|
|
}
|
|
|
|
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 (size_t(peer().send(mb->rd_ptr(), mb->length())) != mb->length())
|
|
{
|
|
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->outDebug(LOG_FILTER_REMOTECOMMAND, "User %s does not exist in database", user.c_str());
|
|
return -1;
|
|
}
|
|
|
|
Field* fields = result->Fetch();
|
|
|
|
if (fields[1].GetUInt8() < _minLevel)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "User %s has no privilege to login", user.c_str());
|
|
return -1;
|
|
}
|
|
else if (fields[2].GetInt32() != -1)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "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->outDebug(LOG_FILTER_REMOTECOMMAND, "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->outDebug(LOG_FILTER_REMOTECOMMAND, "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->outDebug(LOG_FILTER_REMOTECOMMAND, "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->outDebug(LOG_FILTER_REMOTECOMMAND, "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->outDebug(LOG_FILTER_REMOTECOMMAND, ss.str().c_str());
|
|
}
|
|
++i;
|
|
}
|
|
#endif
|
|
|
|
//! Just send back end of subnegotiation packet
|
|
uint8 const reply[2] = {0xFF, 0xF0};
|
|
return peer().send(reply, 2);
|
|
}
|
|
|
|
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
|
|
const char* tc_prompt = "TC> ";
|
|
if (size_t(peer().send(tc_prompt, strlen(tc_prompt))) != strlen(tc_prompt))
|
|
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);
|
|
|
|
if (socket->putq(mb, const_cast<ACE_Time_Value*>(&ACE_Time_Value::zero)) == -1)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "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) == -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();
|
|
}
|
|
}
|