OregonCore  revision 3611e8a-git
Your Favourite TBC server
RASocket.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore 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 "Common.h"
19 #include "Config/Config.h"
20 #include "DatabaseEnv.h"
21 #include "AccountMgr.h"
22 #include "Log.h"
23 #include "RASocket.h"
24 #include "Util.h"
25 #include "World.h"
26 #include "Auth/Sha1.h"
27 
29 {
30  iMinLevel = sConfig.GetIntDefault("RA.MinLevel", 3);
31 }
32 
34 {
35 }
36 
37 int RASocket::open(void*)
38 {
39  ACE_INET_Addr remote_addr;
40 
41  if (peer().get_remote_addr(remote_addr) == -1)
42  {
43  sLog.outError("RASocket::open: peer().get_remote_addr error is %s", ACE_OS::strerror(errno));
44  return -1;
45  }
46 
47  sLog.outRemote("Incoming connection from %s", remote_addr.get_host_addr());
48 
49  return activate();
50 }
51 
52 int RASocket::handle_close(ACE_HANDLE, ACE_Reactor_Mask)
53 {
54  sLog.outRemote("Closing connection");
55  peer().close_reader();
56  wait();
57  destroy();
58  return 0;
59 }
60 
61 int RASocket::send(const std::string& line)
62 {
63  return size_t(peer().send(line.c_str(), line.length())) == line.length() ? 0 : -1;
64 }
65 
66 int RASocket::recv_line(ACE_Message_Block& buffer)
67 {
68  char byte;
69  for (;;)
70  {
71  ssize_t n = peer().recv(&byte, sizeof(byte));
72 
73  if (n < 0)
74  return -1;
75 
76  if (n == 0)
77  {
78  // EOF, connection was closed
79  errno = ECONNRESET;
80  return -1;
81  }
82 
83  ACE_ASSERT(n == sizeof(byte));
84 
85  if (byte == '\n')
86  break;
87  else if (byte == '\r') /* Ignore CR */
88  continue;
89  else if (buffer.copy(&byte, sizeof(byte)) == -1)
90  return -1;
91  }
92 
93  const char null_term = '\0';
94  if (buffer.copy(&null_term, sizeof(null_term)) == -1)
95  return -1;
96 
97  return 0;
98 }
99 
100 int RASocket::recv_line(std::string& out_line)
101 {
102  char buf[4096];
103 
104  ACE_Data_Block db(sizeof (buf),
105  ACE_Message_Block::MB_DATA,
106  buf,
107  0,
108  0,
109  ACE_Message_Block::DONT_DELETE,
110  0);
111 
112  ACE_Message_Block message_block(&db,
113  ACE_Message_Block::DONT_DELETE,
114  0);
115 
116  if (recv_line(message_block) == -1)
117  {
118  sLog.outRemote("Recv error %s", ACE_OS::strerror(errno));
119  return -1;
120  }
121 
122  out_line = message_block.rd_ptr();
123 
124  return 0;
125 }
126 
127 int RASocket::process_command(const std::string& command)
128 {
129  if (command.length() == 0)
130  return 0;
131 
132  sLog.outRemote("Got command: %s", command.c_str());
133 
134  // handle quit, exit and logout commands to terminate connection
135  if (command == "quit" || command == "exit" || command == "logout")
136  {
137  (void) send("Bye\r\n");
138  return -1;
139  }
140 
141  CliCommandHolder* cmd = new CliCommandHolder(this, command.c_str(), &RASocket::zprint, &RASocket::commandFinished);
142  sWorld.QueueCliCommand(cmd);
143 
144  // wait for result
145  ACE_Message_Block* mb;
146  for (;;)
147  {
148  if (getq(mb) == -1)
149  return -1;
150 
151  if (mb->msg_type() == ACE_Message_Block::MB_BREAK)
152  {
153  mb->release();
154  break;
155  }
156 
157  if (size_t(peer().send(mb->rd_ptr(), mb->length())) != mb->length())
158  {
159  mb->release();
160  return -1;
161  }
162 
163  mb->release();
164  }
165 
166  return 0;
167 }
168 
169 int RASocket::check_access_level(const std::string& user)
170 {
171  std::string safe_user = user;
172 
173  AccountMgr::normalizeString(safe_user);
174  LoginDatabase.escape_string(safe_user);
175 
176  QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT a.id, aa.gmlevel, aa.RealmID FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = '%s'", safe_user.c_str());
177 
178  if (!result)
179  {
180  sLog.outRemote("User %s does not exist in database", user.c_str());
181  return -1;
182  }
183 
184  Field* fields = result->Fetch();
185 
186  if (fields[1].GetUInt32() < iMinLevel)
187  {
188  sLog.outRemote("User %s has no privilege to login", user.c_str());
189  return -1;
190  }
191  else if (fields[2].GetInt32() != -1)
192  {
193  sLog.outRemote("User %s has to be assigned on all realms (with RealmID = '-1')", user.c_str());
194  return -1;
195  }
196 
197  return 0;
198 }
199 
200 int RASocket::check_password(const std::string& user, const std::string& pass)
201 {
202  std::string safe_user = user;
203  AccountMgr::normalizeString(safe_user);
204  LoginDatabase.escape_string(safe_user);
205 
206  std::string safe_pass = pass;
207  AccountMgr::normalizeString(safe_pass);
208  LoginDatabase.escape_string(safe_pass);
209 
210  std::string hash = AccountMgr::CalculateShaPassHash(safe_user, safe_pass);
211 
213  "SELECT 1 FROM account WHERE username = '%s' AND sha_pass_hash = '%s'",
214  safe_user.c_str(), hash.c_str());
215 
216  if (!check)
217  {
218  sLog.outRemote("Wrong password for user: %s", user.c_str());
219  return -1;
220  }
221 
222  return 0;
223 }
224 
226 {
227  if (send(std::string("Username: ")) == -1)
228  return -1;
229 
230  std::string user;
231  if (recv_line(user) == -1)
232  return -1;
233 
234  if (send(std::string("Password: ")) == -1)
235  return -1;
236 
237  std::string pass;
238  if (recv_line(pass) == -1)
239  return -1;
240 
241  sLog.outRemote("Login attempt for user: %s", user.c_str());
242 
243  if (check_access_level(user) == -1)
244  return -1;
245 
246  if (check_password(user, pass) == -1)
247  return -1;
248 
249  sLog.outRemote("User login: %s", user.c_str());
250 
251  return 0;
252 }
253 
254 
256 {
257  char buf[1024];
258 
259  ACE_Data_Block db(sizeof (buf),
260  ACE_Message_Block::MB_DATA,
261  buf,
262  0,
263  0,
264  ACE_Message_Block::DONT_DELETE,
265  0);
266 
267  ACE_Message_Block message_block(&db,
268  ACE_Message_Block::DONT_DELETE,
269  0);
270 
271  const size_t recv_size = message_block.space();
272 
273  // Wait a maximum of 1000ms for negotiation packet - not all telnet clients may send it
274  ACE_Time_Value waitTime = ACE_Time_Value(1);
275  const ssize_t n = peer().recv(message_block.wr_ptr(),
276  recv_size, &waitTime);
277 
278  if (n <= 0)
279  return int(n);
280 
281  if (n >= 1024)
282  {
283  sLog.outRemote("RASocket::subnegotiate: allocated buffer 1024 bytes was too small for negotiation packet, size: %u", n);
284  return -1;
285  }
286 
287  buf[n] = '\0';
288 
289  #ifdef _DEBUG
290  for (uint8 i = 0; i < n; )
291  {
292  uint8 iac = buf[i];
293  if (iac == 0xFF) // "Interpret as Command" (IAC)
294  {
295  uint8 command = buf[++i];
296  std::stringstream ss;
297  switch (command)
298  {
299  case 0xFB: // WILL
300  ss << "WILL ";
301  break;
302  case 0xFC: // WON'T
303  ss << "WON'T ";
304  break;
305  case 0xFD: // DO
306  ss << "DO ";
307  break;
308  case 0xFE: // DON'T
309  ss << "DON'T ";
310  break;
311  default:
312  return -1; // not allowed
313  }
314 
315  uint8 param = buf[++i];
316  ss << uint32(param);
317  sLog.outRemote(ss.str().c_str());
318  }
319  ++i;
320  }
321  #endif
322 
324  uint8 const reply[2] = {0xFF, 0xF0};
325  return peer().send(reply, 2);
326 }
327 
328 int RASocket::svc(void)
329 {
331  subnegotiate();
332 
333  if (send("Authentication required\r\n") == -1)
334  return -1;
335 
336  if (authenticate() == -1)
337  {
338  (void) send("Authentication failed\r\n");
339  return -1;
340  }
341 
342  // send motd
343  if (send(std::string(sWorld.GetMotd()) + "\r\n") == -1)
344  return -1;
345 
346  for (;;)
347  {
348  // show prompt
349  const char* tc_prompt = "TC> ";
350  if (size_t(peer().send(tc_prompt, strlen(tc_prompt))) != strlen(tc_prompt))
351  return -1;
352 
353  std::string line;
354 
355  if (recv_line(line) == -1)
356  return -1;
357 
358  if (process_command(line) == -1)
359  return -1;
360  }
361 
362  return 0;
363 }
364 
365 void RASocket::zprint(void* callbackArg, const char* szText)
366 {
367  if (!szText || !callbackArg)
368  return;
369 
370  RASocket* socket = static_cast<RASocket*>(callbackArg);
371  size_t sz = strlen(szText);
372 
373  ACE_Message_Block* mb = new ACE_Message_Block(sz);
374  mb->copy(szText, sz);
375 
376  if (socket->putq(mb, const_cast<ACE_Time_Value*>(&ACE_Time_Value::zero)) == -1)
377  {
378  sLog.outRemote("Failed to enqueue message, queue is full or closed. Error is %s", ACE_OS::strerror(errno));
379  mb->release();
380  }
381 }
382 
383 void RASocket::commandFinished(void* callbackArg, bool /*success*/)
384 {
385  if (!callbackArg)
386  return;
387 
388  RASocket* socket = static_cast<RASocket*>(callbackArg);
389 
390  ACE_Message_Block* mb = new ACE_Message_Block();
391 
392  mb->msg_type(ACE_Message_Block::MB_BREAK);
393 
394  // the message is 0 size control message to tell that command output is finished
395  // hence we don't put timeout, because it shouldn't increase queue size and shouldn't block
396  if (socket->putq(mb) == -1)
397  {
398  // getting here is bad, command can't be marked as complete
399  sLog.outRemote("Failed to enqueue command end message. Error is %s", ACE_OS::strerror(errno));
400  mb->release();
401  }
402 }
int send(const std::string &line)
Definition: RASocket.cpp:61
#define sConfig
Definition: Config.h:52
static void zprint(void *callbackArg, const char *szText)
Definition: RASocket.cpp:365
static void commandFinished(void *callbackArg, bool success)
Definition: RASocket.cpp:383
QueryResult_AutoPtr PQuery(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:400
Definition: Field.h:24
static std::string CalculateShaPassHash(std::string &name, std::string &password)
Definition: AccountMgr.cpp:236
virtual int open(void *=0)
Definition: RASocket.cpp:37
#define sLog
Log class singleton.
Definition: Log.h:187
int process_command(const std::string &command)
Definition: RASocket.cpp:127
unsigned long escape_string(char *to, const char *from, unsigned long length)
Definition: Database.cpp:212
int check_password(const std::string &user, const std::string &pass)
Definition: RASocket.cpp:200
Remote Administration socket.
Definition: RASocket.h:29
ACE_UINT8 uint8
Definition: Define.h:73
virtual ~RASocket()
Definition: RASocket.cpp:33
DatabaseType LoginDatabase
Accessor to the realm/login database.
Definition: Main.cpp:55
static bool normalizeString(std::string &utf8str)
Definition: AccountMgr.cpp:223
int check_access_level(const std::string &user)
Used by telnet protocol RFC 854 / 855.
Definition: RASocket.cpp:169
virtual int svc(void)
Definition: RASocket.cpp:328
int recv_line(std::string &out_line)
Definition: RASocket.cpp:100
ACE_Refcounted_Auto_Ptr< QueryResult, ACE_Null_Mutex > QueryResult_AutoPtr
Definition: QueryResult.h:113
uint8 iMinLevel
Minimum security level required to connect.
Definition: RASocket.h:54
RASocket()
Definition: RASocket.cpp:28
virtual int handle_close(ACE_HANDLE=ACE_INVALID_HANDLE, ACE_Reactor_Mask=ACE_Event_Handler::ALL_EVENTS_MASK)
Definition: RASocket.cpp:52
int authenticate()
Definition: RASocket.cpp:225
#define sWorld
Definition: World.h:860
ACE_UINT32 uint32
Definition: Define.h:71
int subnegotiate()
Definition: RASocket.cpp:255