TrinityCore
Loading...
Searching...
No Matches
RASession.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "RASession.h"
19#include "AccountMgr.h"
20#include "Config.h"
21#include "DatabaseEnv.h"
22#include "Log.h"
23#include "SRP6.h"
24#include "Util.h"
25#include "World.h"
26#include <boost/asio/buffer.hpp>
27#include <boost/asio/read_until.hpp>
28#include <memory>
29#include <thread>
30
31using boost::asio::ip::tcp;
32
34{
35 _socket.non_blocking(false);
36
37 // wait 1 second for active connections to send negotiation request
38 for (int counter = 0; counter < 10 && _socket.available() == 0; counter++)
39 std::this_thread::sleep_for(100ms);
40
41 // Check if there are bytes available, if they are, then the client is requesting the negotiation
42 if (_socket.available() > 0)
43 {
44 // Handle subnegotiation
45 char buf[1024] = { };
46 _socket.read_some(boost::asio::buffer(buf));
47
48 // Send the end-of-negotiation packet
49 uint8 const reply[2] = { 0xFF, 0xF0 };
50 _socket.write_some(boost::asio::buffer(reply));
51 }
52
53 Send("Authentication Required\r\n");
54 Send("Username: ");
55
56 std::string username = ReadString();
57
58 if (username.empty())
59 return;
60
61 TC_LOG_INFO("commands.ra", "Accepting RA connection from user {} (IP: {})", username, GetRemoteIpAddress());
62
63 Send("Password: ");
64
65 std::string password = ReadString();
66 if (password.empty())
67 return;
68
69 if (!CheckAccessLevel(username) || !CheckPassword(username, password))
70 {
71 Send("Authentication failed\r\n");
72 _socket.close();
73 return;
74 }
75
76 TC_LOG_INFO("commands.ra", "User {} (IP: {}) authenticated correctly to RA", username, GetRemoteIpAddress());
77
78 // Authentication successful, send the motd
79 for (std::string const& line : sWorld->GetMotd())
80 Send(line.c_str());
81 Send("\r\n");
82
83 // Read commands
84 for (;;)
85 {
86 Send("TC>");
87 std::string command = ReadString();
88
89 if (ProcessCommand(command))
90 break;
91 }
92
93 _socket.close();
94}
95
96int RASession::Send(std::string_view data)
97{
98 std::ostream os(&_writeBuffer);
99 os << data;
100 size_t written = _socket.send(_writeBuffer.data());
101 _writeBuffer.consume(written);
102 return written;
103}
104
106{
107 boost::system::error_code error;
108 size_t read = boost::asio::read_until(_socket, _readBuffer, "\r\n", error);
109 if (!read)
110 {
111 _socket.close();
112 return "";
113 }
114
115 std::string line;
116 std::istream is(&_readBuffer);
117 std::getline(is, line);
118
119 if (*line.rbegin() == '\r')
120 line.erase(line.length() - 1);
121
122 return line;
123}
124
125bool RASession::CheckAccessLevel(const std::string& user)
126{
127 std::string safeUser = user;
128
129 Utf8ToUpperOnlyLatin(safeUser);
130
132 stmt->setString(0, safeUser);
133 PreparedQueryResult result = LoginDatabase.Query(stmt);
134
135 if (!result)
136 {
137 TC_LOG_INFO("commands.ra", "User {} does not exist in database", user);
138 return false;
139 }
140
141 Field* fields = result->Fetch();
142
143 if (fields[1].GetUInt8() < sConfigMgr->GetIntDefault("Ra.MinLevel", 3))
144 {
145 TC_LOG_INFO("commands.ra", "User {} has no privilege to login", user);
146 return false;
147 }
148 else if (fields[2].GetInt32() != -1)
149 {
150 TC_LOG_INFO("commands.ra", "User {} has to be assigned on all realms (with RealmID = '-1')", user);
151 return false;
152 }
153
154 return true;
155}
156
157bool RASession::CheckPassword(const std::string& user, const std::string& pass)
158{
159 std::string safe_user = user;
160 std::transform(safe_user.begin(), safe_user.end(), safe_user.begin(), ::toupper);
161 Utf8ToUpperOnlyLatin(safe_user);
162
163 std::string safe_pass = pass;
164 Utf8ToUpperOnlyLatin(safe_pass);
165 std::transform(safe_pass.begin(), safe_pass.end(), safe_pass.begin(), ::toupper);
166
168
169 stmt->setString(0, safe_user);
170
171 if (PreparedQueryResult result = LoginDatabase.Query(stmt))
172 {
175
176 if (Trinity::Crypto::SRP6::CheckLogin(safe_user, safe_pass, salt, verifier))
177 return true;
178 }
179
180 TC_LOG_INFO("commands.ra", "Wrong password for user: {}", user);
181 return false;
182}
183
184bool RASession::ProcessCommand(std::string& command)
185{
186 if (command.length() == 0)
187 return true;
188
189 TC_LOG_INFO("commands.ra", "Received command: {}", command);
190
191 // handle quit, exit and logout commands to terminate connection
192 if (command == "quit" || command == "exit" || command == "logout")
193 {
194 Send("Bye\r\n");
195 return true;
196 }
197
198 // Obtain a new promise per command
199 delete _commandExecuting;
200 _commandExecuting = new std::promise<void>();
201
203 sWorld->QueueCliCommand(cmd);
204
205 // Wait for the command to finish
206 _commandExecuting->get_future().wait();
207
208 return false;
209}
210
211void RASession::CommandPrint(void* callbackArg, std::string_view text)
212{
213 if (text.empty())
214 return;
215
216 RASession* session = static_cast<RASession*>(callbackArg);
217 session->Send(text);
218}
219
220void RASession::CommandFinished(void* callbackArg, bool /*success*/)
221{
222 RASession* session = static_cast<RASession*>(callbackArg);
223 session->_commandExecuting->set_value();
224}
#define sConfigMgr
Definition Config.h:60
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
uint8_t uint8
Definition Define.h:135
#define TC_LOG_INFO(filterType__,...)
Definition Log.h:159
@ LOGIN_SEL_CHECK_PASSWORD_BY_NAME
@ LOGIN_SEL_ACCOUNT_ACCESS
bool Utf8ToUpperOnlyLatin(std::string &utf8String)
Definition Util.cpp:610
Class used to access individual fields of database query result.
Definition Field.h:92
void setString(uint8 index, std::string const &value)
int Send(std::string_view data)
Definition RASession.cpp:96
static void CommandFinished(void *callbackArg, bool)
std::string ReadString()
bool CheckAccessLevel(const std::string &user)
std::promise< void > * _commandExecuting
Definition RASession.h:58
boost::asio::streambuf _writeBuffer
Definition RASession.h:57
boost::asio::streambuf _readBuffer
Definition RASession.h:56
Trinity::Net::IoContextTcpSocket _socket
Definition RASession.h:55
bool CheckPassword(const std::string &user, const std::string &pass)
bool ProcessCommand(std::string &command)
static void CommandPrint(void *callbackArg, std::string_view text)
void Start()
Definition RASession.cpp:33
const std::string GetRemoteIpAddress() const
Definition RASession.h:42
static constexpr size_t SALT_LENGTH
Definition SRP6.h:34
static constexpr size_t VERIFIER_LENGTH
Definition SRP6.h:36
std::array< uint8, SALT_LENGTH > Salt
Definition SRP6.h:35
std::array< uint8, VERIFIER_LENGTH > Verifier
Definition SRP6.h:37
static bool CheckLogin(std::string const &username, std::string const &password, Salt const &salt, Verifier const &verifier)
Definition SRP6.h:47
#define sWorld
Definition World.h:900
Storage class for commands issued for delayed execution.
Definition World.h:536