Repo-Init
 
Loading...
Searching...
No Matches
TelnetSession Class Reference

#include <TelnetServer.hpp>

Inheritance diagram for TelnetSession:
Collaboration diagram for TelnetSession:

Public Member Functions

 TelnetSession (Socket ClientSocket, std::shared_ptr< TelnetServer > tServer)
 Constructor for session.
 
void sendLine (std::string data)
 Send a line of data to the Telnet Server.
 
void closeClient ()
 Finish the session.
 
bool checkTimeout () const
 Checks the connection timeout.
 
void markTimeout ()
 Marks timeout to close session.
 

Protected Member Functions

void initialise ()
 Initialise session.
 
void update ()
 Called every frame/loop by the Terminal Server.
 

Private Member Functions

std::string getPeerIP () const
 
void sendPromptAndBuffer ()
 
void eraseLine ()
 
void echoBack (const char *buffer, unsigned long length)
 
bool processTab (std::string &buffer)
 
void addToHistory (const std::string &line)
 
bool processCommandHistory (std::string &buffer)
 

Static Private Member Functions

static void stripNVT (std::string &buffer)
 
static void stripEscapeCharacters (std::string &buffer)
 
static bool processBackspace (std::string &buffer)
 
static std::vector< std::string > getCompleteLines (std::string &buffer)
 

Private Attributes

TelnetSessionStats stats
 Statistics variables.
 
std::chrono::system_clock::time_point lastSeenTime
 
Socket m_socket
 
std::shared_ptr< TelnetServerm_telnetServer
 
std::string m_buffer
 
std::list< std::string > m_history
 
std::list< std::string >::iterator m_historyCursor
 
friend TelnetServer
 

Detailed Description

Session class for manage connections

Definition at line 56 of file TelnetServer.hpp.

Constructor & Destructor Documentation

◆ TelnetSession()

TelnetSession::TelnetSession ( Socket ClientSocket,
std::shared_ptr< TelnetServer > tServer )
inline

Constructor for session.

Definition at line 59 of file TelnetServer.hpp.

60 : m_socket(ClientSocket), m_telnetServer(std::move(tServer))
61 {
63 };
std::shared_ptr< TelnetServer > m_telnetServer
std::list< std::string > m_history
std::list< std::string >::iterator m_historyCursor

Member Function Documentation

◆ addToHistory()

void TelnetSession::addToHistory ( const std::string & line)
private

Definition at line 362 of file TelnetServer.cpp.

363{
364 // Add it to the history
365 if (line != (!m_history.empty() ? m_history.back() : "") && !line.empty())
366 {
367 m_history.push_back(line);
368 if (m_history.size() > TELNET_HISTORY_LIMIT)
369 {
370 m_history.pop_front();
371 }
372 }
374}
constexpr int TELNET_HISTORY_LIMIT
Here is the caller graph for this function:

◆ checkTimeout()

bool TelnetSession::checkTimeout ( ) const

Checks the connection timeout.

Definition at line 194 of file TelnetServer.cpp.

195{
196 return (llabs(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - lastSeenTime)
197 .count()) > TELNET_TIMEOUT);
198}
constexpr int TELNET_TIMEOUT
std::chrono::system_clock::time_point lastSeenTime

◆ closeClient()

void TelnetSession::closeClient ( )

Finish the session.

Definition at line 180 of file TelnetServer.cpp.

181{
182 spdlog::info("Telnet connection to {} closed", getPeerIP());
183
184 // Attempt to cleanly shutdown the connection since we're done
185 shutdown(m_socket, SHUT_WR);
186
187 // Cleanup
188 close(m_socket);
189
190 // Set disconnect time
191 stats.disconnectTime = std::chrono::high_resolution_clock::now();
192}
TelnetSessionStats stats
Statistics variables.
std::string getPeerIP() const
std::chrono::high_resolution_clock::time_point disconnectTime
Connection end time.
Here is the call graph for this function:

◆ echoBack()

void TelnetSession::echoBack ( const char * buffer,
unsigned long length )
private

Definition at line 205 of file TelnetServer.cpp.

206{
207 // If you are an NVT command (i.e. first it of data is 255) then ignore the echo back
208 if (static_cast<uint8_t>(*buffer) == ASCII_NBSP)
209 {
210 return;
211 }
212
213 const ssize_t sendBytes = send(m_socket, buffer, length, 0);
214 if (sendBytes > 0)
215 {
216 stats.uploadBytes += static_cast<size_t>(sendBytes);
217 }
218}
constexpr int ASCII_NBSP
size_t uploadBytes
Uploaded bytes.
Here is the caller graph for this function:

◆ eraseLine()

void TelnetSession::eraseLine ( )
private

Definition at line 142 of file TelnetServer.cpp.

143{
144 // Send an erase line
145 ssize_t sendBytes = send(m_socket, ANSI_ERASE_LINE.c_str(), ANSI_ERASE_LINE.length(), 0);
146 if (sendBytes > 0)
147 {
148 stats.uploadBytes += static_cast<size_t>(sendBytes);
149 }
150
151 // Move the cursor to the beginning of the line
152 const std::string moveBack = "\x1b[80D";
153 sendBytes = send(m_socket, moveBack.c_str(), moveBack.length(), 0);
154 if (sendBytes > 0)
155 {
156 stats.uploadBytes += static_cast<size_t>(sendBytes);
157 }
158}
const std::string ANSI_ERASE_LINE("\x1b[2K")
Here is the call graph for this function:
Here is the caller graph for this function:

◆ getCompleteLines()

std::vector< std::string > TelnetSession::getCompleteLines ( std::string & buffer)
staticprivate

Definition at line 426 of file TelnetServer.cpp.

427{
428 // Now find all new lines (<CR><LF>) and place in a vector and delete from buffer
429 std::vector<std::string> lines;
430
431 size_t found = 0;
432 do
433 {
434 found = buffer.find("\r\n");
435 if (found != std::string::npos)
436 {
437 lines.push_back(buffer.substr(0, found));
438 buffer.erase(0, found + 2);
439 }
440 } while (found != std::string::npos);
441
442 return lines;
443}
Here is the caller graph for this function:

◆ getPeerIP()

std::string TelnetSession::getPeerIP ( ) const
private

Definition at line 108 of file TelnetServer.cpp.

109{
110 sockaddr_in client_info{};
111 memset(&client_info, 0, sizeof(client_info));
112 socklen_t addrsize = sizeof(client_info);
113 getpeername(m_socket, reinterpret_cast<sockaddr *>(&client_info), &addrsize);
114
115 std::array<char, INET_ADDRSTRLEN> ipAddr{};
116 inet_ntop(AF_INET, &client_info.sin_addr, ipAddr.data(), INET_ADDRSTRLEN);
117
118 return ipAddr.data();
119}
Here is the caller graph for this function:

◆ initialise()

void TelnetSession::initialise ( )
protected

Initialise session.

Definition at line 220 of file TelnetServer.cpp.

221{
222 // Get details of connection
223 spdlog::info("Telnet connection received from {}", getPeerIP());
224
225 stats.connectTime = std::chrono::high_resolution_clock::now();
226
227 // Set the connection to be non-blocking
228 unsigned long iMode = 1;
229 ioctl(m_socket, FIONBIO, &iMode);
230
231 // Set NVT mode to say that I will echo back characters.
232 const std::array<uint8_t, 3> willEcho{0xff, 0xfb, 0x01};
233 ssize_t sendBytes = send(m_socket, willEcho.data(), 3, 0);
234 if (sendBytes > 0)
235 {
236 stats.uploadBytes += static_cast<size_t>(sendBytes);
237 }
238
239 // Set NVT requesting that the remote system not/dont echo back characters
240 const std::array<uint8_t, 3> dontEcho{0xff, 0xfe, 0x01};
241 sendBytes = send(m_socket, dontEcho.data(), 3, 0);
242 if (sendBytes > 0)
243 {
244 stats.uploadBytes += static_cast<size_t>(sendBytes);
245 }
246
247 // Set NVT mode to say that I will suppress go-ahead. Stops remote clients from doing local linemode.
248 const std::array<uint8_t, 3> willSGA{0xff, 0xfb, 0x03};
249 sendBytes = send(m_socket, willSGA.data(), 3, 0);
250 if (sendBytes > 0)
251 {
252 stats.uploadBytes += static_cast<size_t>(sendBytes);
253 }
254
255 if (m_telnetServer->connectedCallback())
256 {
257 m_telnetServer->connectedCallback()(shared_from_this());
258 }
259
260 // Set last seen
261 lastSeenTime = std::chrono::system_clock::now();
262}
std::chrono::high_resolution_clock::time_point connectTime
Connection start time.
Here is the call graph for this function:

◆ markTimeout()

void TelnetSession::markTimeout ( )

Marks timeout to close session.

Definition at line 200 of file TelnetServer.cpp.

201{
202 lastSeenTime = std::chrono::system_clock::time_point(std::chrono::duration<int>(0));
203}

◆ processBackspace()

bool TelnetSession::processBackspace ( std::string & buffer)
staticprivate

Definition at line 299 of file TelnetServer.cpp.

300{
301 bool foundBackspaces = false;
302
303 size_t found = 0;
304 do
305 {
306 // Need to handle both \x7f and \b backspaces
307 found = buffer.find_first_of('\x7f');
308 if (found == std::string::npos)
309 {
310 found = buffer.find_first_of('\b');
311 }
312
313 if (found != std::string::npos)
314 {
315 if (buffer.length() > 1 && (found > 0))
316 {
317 buffer.erase(found - 1, 2);
318 }
319 else
320 {
321 buffer = "";
322 }
323 foundBackspaces = true;
324 }
325 } while (found != std::string::npos);
326 return foundBackspaces;
327}
Here is the caller graph for this function:

◆ processCommandHistory()

bool TelnetSession::processCommandHistory ( std::string & buffer)
private

Definition at line 376 of file TelnetServer.cpp.

377{
378 // Handle up and down arrow actions
379 if (m_telnetServer->interactivePrompt())
380 {
381 if (buffer.find(ANSI_ARROW_UP) != std::string::npos && !m_history.empty())
382 {
383 if (m_historyCursor != m_history.begin())
384 {
386 }
387 buffer = *m_historyCursor;
388
389 // Issue a cursor command to counter it
390 ssize_t sendBytes = 0;
391 if ((sendBytes = send(m_socket, ANSI_ARROW_DOWN.c_str(), ANSI_ARROW_DOWN.length(), 0)) < 0)
392 {
393 return false;
394 }
395 stats.uploadBytes += static_cast<size_t>(sendBytes);
396 return true;
397 }
398 if (buffer.find(ANSI_ARROW_DOWN) != std::string::npos && !m_history.empty())
399 {
400 if (next(m_historyCursor) != m_history.end())
401 {
403 }
404 buffer = *m_historyCursor;
405
406 // Issue a cursor command to counter it
407 ssize_t sendBytes = 0;
408 if ((sendBytes = send(m_socket, ANSI_ARROW_UP.c_str(), ANSI_ARROW_UP.length(), 0)) < 0)
409 {
410 return false;
411 }
412
413 stats.uploadBytes += static_cast<size_t>(sendBytes);
414 return true;
415 }
416
417 // Ignore left and right and just reprint buffer
418 if (buffer.find(ANSI_ARROW_LEFT) != std::string::npos || buffer.find(ANSI_ARROW_RIGHT) != std::string::npos)
419 {
420 return true;
421 }
422 }
423 return false;
424}
const std::string ANSI_ARROW_RIGHT("\x1b\x5b\x43")
const std::string ANSI_ARROW_DOWN("\x1b\x5b\x42")
const std::string ANSI_ARROW_UP("\x1b\x5b\x41")
const std::string ANSI_ARROW_LEFT("\x1b\x5b\x44")
Here is the call graph for this function:
Here is the caller graph for this function:

◆ processTab()

bool TelnetSession::processTab ( std::string & buffer)
private

Definition at line 329 of file TelnetServer.cpp.

330{
331 bool foundTabs = false;
332
333 size_t found = 0;
334 do
335 {
336 found = buffer.find_first_of('\t');
337 if (found != std::string::npos)
338 {
339 // Remove single tab
340 if (buffer.length() > 0)
341 {
342 buffer.erase(found, 1);
343 }
344 foundTabs = true;
345
346 // Process
347 if (m_telnetServer->tabCallback())
348 {
349 const std::string retCommand =
350 m_telnetServer->tabCallback()(shared_from_this(), buffer.substr(0, found));
351 if (!retCommand.empty())
352 {
353 buffer.erase(0, found);
354 buffer.insert(0, retCommand);
355 }
356 }
357 }
358 } while (found != std::string::npos);
359 return foundTabs;
360}
Here is the caller graph for this function:

◆ sendLine()

void TelnetSession::sendLine ( std::string data)

Send a line of data to the Telnet Server.

Definition at line 160 of file TelnetServer.cpp.

161{
162 // If is something is on the prompt, wipe it off
163 if (m_telnetServer->interactivePrompt() || m_buffer.length() > 0)
164 {
165 eraseLine();
166 }
167
168 data.append("\r\n");
169 if (auto sendBytes = send(m_socket, data.c_str(), data.length(), 0) > 0)
170 {
171 stats.uploadBytes += static_cast<size_t>(sendBytes);
172 }
173
174 if (m_telnetServer->interactivePrompt())
175 {
177 }
178}
std::string m_buffer
void sendPromptAndBuffer()
Here is the call graph for this function:

◆ sendPromptAndBuffer()

void TelnetSession::sendPromptAndBuffer ( )
private

Definition at line 121 of file TelnetServer.cpp.

122{
123 // Output the prompt
124 ssize_t sendBytes =
125 send(m_socket, m_telnetServer->promptString().c_str(), m_telnetServer->promptString().length(), 0);
126 if (sendBytes > 0)
127 {
128 stats.uploadBytes += static_cast<size_t>(sendBytes);
129 }
130
131 // Resend the buffer
132 if (m_buffer.length() > 0)
133 {
134 sendBytes = send(m_socket, m_buffer.c_str(), m_buffer.length(), 0);
135 if (sendBytes > 0)
136 {
137 stats.uploadBytes += static_cast<size_t>(sendBytes);
138 }
139 }
140}
Here is the caller graph for this function:

◆ stripEscapeCharacters()

void TelnetSession::stripEscapeCharacters ( std::string & buffer)
staticprivate

Definition at line 281 of file TelnetServer.cpp.

282{
283 size_t found = 0;
284 const std::array<std::string, 4> cursors = {ANSI_ARROW_UP, ANSI_ARROW_DOWN, ANSI_ARROW_RIGHT, ANSI_ARROW_LEFT};
285
286 for (const auto &cursor : cursors)
287 {
288 do
289 {
290 found = buffer.find(cursor);
291 if (found != std::string::npos)
292 {
293 buffer.erase(found, cursor.length());
294 }
295 } while (found != std::string::npos);
296 }
297}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ stripNVT()

void TelnetSession::stripNVT ( std::string & buffer)
staticprivate

Definition at line 264 of file TelnetServer.cpp.

265{
266 size_t found = 0;
267 do
268 {
269 found = buffer.find_first_of(static_cast<char>(ASCII_NBSP));
270 if (found != std::string::npos && (found + 2) <= buffer.length() - 1)
271 {
272 buffer.erase(found, 3);
273 }
274 else if ((found + 2) >= buffer.length())
275 {
276 break;
277 }
278 } while (found != std::string::npos);
279}
Here is the caller graph for this function:

◆ update()

void TelnetSession::update ( )
protected

Called every frame/loop by the Terminal Server.

Definition at line 445 of file TelnetServer.cpp.

446{
447 ssize_t readBytes = 0;
448 std::array<char, DEFAULT_BUFLEN> recvbuf{};
449
450 // Reset stats
451 stats.uploadBytes = 0;
454 stats.failCmdCtr = 0;
455
456 // Receive
457 readBytes = recv(m_socket, recvbuf.data(), DEFAULT_BUFLEN, 0);
458
459 // Check for errors from the read
460 if (readBytes < 0 && errno != EAGAIN)
461 {
462 close(m_socket);
463 }
464 else if (readBytes > 0)
465 {
466 stats.downloadBytes += static_cast<size_t>(readBytes);
467
468 // Update last seen
469 lastSeenTime = std::chrono::system_clock::now();
470
471 // Echo it back to the sender
472 echoBack(recvbuf.data(), static_cast<size_t>(readBytes));
473
474 // we've got to be careful here. Telnet client might send null characters for New Lines mid-data block. We need
475 // to swap these out. recv is not null terminated, so its cool
476 for (size_t i = 0; i < static_cast<size_t>(readBytes); i++)
477 {
478 if (recvbuf[i] == ASCII_NULL) // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
479 {
480 // New Line constant
481 recvbuf[i] = ASCII_LF; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
482 }
483 }
484
485 // Add it to the received buffer
486 m_buffer.append(recvbuf.data(), static_cast<unsigned int>(readBytes));
487 // Remove telnet negotiation sequences
489
490 bool requirePromptReprint = false;
491 if (m_telnetServer->interactivePrompt())
492 {
493 // Read up and down arrow keys and scroll through history
495 {
496 requirePromptReprint = true;
497 }
499
500 // Remove characters
502 {
503 requirePromptReprint = true;
504 }
505 // Complete commands
506 if (processTab(m_buffer))
507 {
508 requirePromptReprint = true;
509 }
510 }
511
512 // Process commands
513 auto lines = getCompleteLines(m_buffer);
514 for (const auto &line : lines)
515 {
516 if (m_telnetServer->newLineCallBack())
517 {
518 if (m_telnetServer->newLineCallBack()(shared_from_this(), line))
519 {
521 }
522 else
523 {
525 }
526 addToHistory(line);
527 }
528 }
529
530 if (m_telnetServer->interactivePrompt() && requirePromptReprint)
531 {
532 eraseLine();
534 }
535 }
536}
constexpr int ASCII_NULL
constexpr int DEFAULT_BUFLEN
constexpr int ASCII_LF
bool processTab(std::string &buffer)
bool processCommandHistory(std::string &buffer)
static bool processBackspace(std::string &buffer)
static void stripEscapeCharacters(std::string &buffer)
static void stripNVT(std::string &buffer)
static std::vector< std::string > getCompleteLines(std::string &buffer)
void echoBack(const char *buffer, unsigned long length)
void addToHistory(const std::string &line)
uint64_t failCmdCtr
Failed commands.
uint64_t successCmdCtr
Successful commands.
size_t downloadBytes
Downloaded bytes.
Here is the call graph for this function:

Member Data Documentation

◆ lastSeenTime

std::chrono::system_clock::time_point TelnetSession::lastSeenTime
private

Definition at line 108 of file TelnetServer.hpp.

◆ m_buffer

std::string TelnetSession::m_buffer
private

Definition at line 114 of file TelnetServer.hpp.

◆ m_history

std::list<std::string> TelnetSession::m_history
private

Definition at line 116 of file TelnetServer.hpp.

◆ m_historyCursor

std::list<std::string>::iterator TelnetSession::m_historyCursor
private

Definition at line 118 of file TelnetServer.hpp.

◆ m_socket

Socket TelnetSession::m_socket
private

Definition at line 110 of file TelnetServer.hpp.

◆ m_telnetServer

std::shared_ptr<TelnetServer> TelnetSession::m_telnetServer
private

Definition at line 112 of file TelnetServer.hpp.

◆ stats

TelnetSessionStats TelnetSession::stats
private

Statistics variables.

Definition at line 106 of file TelnetServer.hpp.

◆ TelnetServer

friend TelnetSession::TelnetServer
private

Definition at line 120 of file TelnetServer.hpp.


The documentation for this class was generated from the following files: