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 366 of file TelnetServer.cpp.

367{
368 // Add it to the history
369 if (line != (!m_history.empty() ? m_history.back() : "") && !line.empty())
370 {
371 m_history.push_back(line);
372 if (m_history.size() > TELNET_HISTORY_LIMIT)
373 {
374 m_history.pop_front();
375 }
376 }
378}
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 197 of file TelnetServer.cpp.

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

◆ closeClient()

void TelnetSession::closeClient ( )

Finish the session.

Definition at line 183 of file TelnetServer.cpp.

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

◆ echoBack()

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

Definition at line 208 of file TelnetServer.cpp.

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

◆ eraseLine()

void TelnetSession::eraseLine ( )
private

Definition at line 145 of file TelnetServer.cpp.

146{
147 // Send an erase line
148 ssize_t sendBytes = send(m_socket, ANSI_ERASE_LINE.c_str(), ANSI_ERASE_LINE.length(), 0);
149 if (sendBytes > 0)
150 {
151 stats.uploadBytes += static_cast<size_t>(sendBytes);
152 }
153
154 // Move the cursor to the beginning of the line
155 const std::string moveBack = "\x1b[80D";
156 sendBytes = send(m_socket, moveBack.c_str(), moveBack.length(), 0);
157 if (sendBytes > 0)
158 {
159 stats.uploadBytes += static_cast<size_t>(sendBytes);
160 }
161}
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 430 of file TelnetServer.cpp.

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

◆ getPeerIP()

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

Definition at line 111 of file TelnetServer.cpp.

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

◆ initialise()

void TelnetSession::initialise ( )
protected

Initialise session.

Definition at line 223 of file TelnetServer.cpp.

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

◆ markTimeout()

void TelnetSession::markTimeout ( )

Marks timeout to close session.

Definition at line 203 of file TelnetServer.cpp.

204{
205 lastSeenTime = std::chrono::system_clock::time_point(std::chrono::duration<int>(0));
206}

◆ processBackspace()

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

Definition at line 302 of file TelnetServer.cpp.

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

◆ processCommandHistory()

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

Definition at line 380 of file TelnetServer.cpp.

381{
382 // Handle up and down arrow actions
383 if (m_telnetServer->interactivePrompt())
384 {
385 if (buffer.find(ANSI_ARROW_UP) != std::string::npos && !m_history.empty())
386 {
387 if (m_historyCursor != m_history.begin())
388 {
390 }
391 buffer = *m_historyCursor;
392
393 // Issue a cursor command to counter it
394 ssize_t sendBytes = 0;
395 if ((sendBytes = send(m_socket, ANSI_ARROW_DOWN.c_str(), ANSI_ARROW_DOWN.length(), 0)) < 0)
396 {
397 return false;
398 }
399 stats.uploadBytes += static_cast<size_t>(sendBytes);
400 return true;
401 }
402 if (buffer.find(ANSI_ARROW_DOWN) != std::string::npos && !m_history.empty())
403 {
404 if (next(m_historyCursor) != m_history.end())
405 {
407 }
408 buffer = *m_historyCursor;
409
410 // Issue a cursor command to counter it
411 ssize_t sendBytes = 0;
412 if ((sendBytes = send(m_socket, ANSI_ARROW_UP.c_str(), ANSI_ARROW_UP.length(), 0)) < 0)
413 {
414 return false;
415 }
416
417 stats.uploadBytes += static_cast<size_t>(sendBytes);
418 return true;
419 }
420
421 // Ignore left and right and just reprint buffer
422 if (buffer.find(ANSI_ARROW_LEFT) != std::string::npos || buffer.find(ANSI_ARROW_RIGHT) != std::string::npos)
423 {
424 return true;
425 }
426 }
427 return false;
428}
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 332 of file TelnetServer.cpp.

333{
334 bool foundTabs = false;
335
336 size_t found = 0;
337 do
338 {
339 found = buffer.find_first_of('\t');
340 if (found == std::string::npos)
341 {
342 continue;
343 }
344
345 // Remove single tab
346 if (!buffer.empty())
347 {
348 buffer.erase(found, 1);
349 }
350 foundTabs = true;
351
352 // Process
353 if (m_telnetServer->tabCallback())
354 {
355 const std::string retCommand = m_telnetServer->tabCallback()(shared_from_this(), buffer.substr(0, found));
356 if (!retCommand.empty())
357 {
358 buffer.erase(0, found);
359 buffer.insert(0, retCommand);
360 }
361 }
362 } while (found != std::string::npos);
363 return foundTabs;
364}
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 163 of file TelnetServer.cpp.

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

◆ sendPromptAndBuffer()

void TelnetSession::sendPromptAndBuffer ( )
private

Definition at line 124 of file TelnetServer.cpp.

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

◆ stripEscapeCharacters()

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

Definition at line 284 of file TelnetServer.cpp.

285{
286 size_t found = 0;
287 const std::array<std::string, 4> cursors = {ANSI_ARROW_UP, ANSI_ARROW_DOWN, ANSI_ARROW_RIGHT, ANSI_ARROW_LEFT};
288
289 for (const auto &cursor : cursors)
290 {
291 do
292 {
293 found = buffer.find(cursor);
294 if (found != std::string::npos)
295 {
296 buffer.erase(found, cursor.length());
297 }
298 } while (found != std::string::npos);
299 }
300}
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 267 of file TelnetServer.cpp.

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

◆ update()

void TelnetSession::update ( )
protected

Called every frame/loop by the Terminal Server.

Definition at line 449 of file TelnetServer.cpp.

450{
451 ssize_t readBytes = 0;
452 std::array<char, DEFAULT_BUFLEN> recvbuf{};
453
454 // Reset stats
455 stats.uploadBytes = 0;
456 stats.downloadBytes = 0;
457 stats.successCmdCtr = 0;
458 stats.failCmdCtr = 0;
459
460 // Receive
461 readBytes = recv(m_socket, recvbuf.data(), DEFAULT_BUFLEN, 0);
462
463 // Check for errors from the read
464 if (readBytes < 0 && errno != EAGAIN)
465 {
466 close(m_socket);
467 }
468 else if (readBytes > 0)
469 {
470 stats.downloadBytes += static_cast<size_t>(readBytes);
471
472 // Update last seen
473 lastSeenTime = std::chrono::system_clock::now();
474
475 // Echo it back to the sender
476 echoBack(recvbuf.data(), static_cast<size_t>(readBytes));
477
478 // we've got to be careful here. Telnet client might send null characters for New Lines mid-data block. We need
479 // to swap these out. recv is not null terminated, so its cool
480 std::replace_if(
481 recvbuf.begin(), recvbuf.begin() + readBytes, [](char chr) { return chr == ASCII_NULL; }, ASCII_LF);
482
483 // Add it to the received buffer
484 m_buffer.append(recvbuf.data(), static_cast<unsigned int>(readBytes));
485 // Remove telnet negotiation sequences
487
488 bool requirePromptReprint = false;
489 if (m_telnetServer->interactivePrompt())
490 {
491 // Read up and down arrow keys and scroll through history
493 {
494 requirePromptReprint = true;
495 }
497
498 // Remove characters
500 {
501 requirePromptReprint = true;
502 }
503 // Complete commands
504 if (processTab(m_buffer))
505 {
506 requirePromptReprint = true;
507 }
508 }
509
510 // Process commands
511 auto lines = getCompleteLines(m_buffer);
512 for (const auto &line : lines)
513 {
514 if (!m_telnetServer->newLineCallBack())
515 {
516 break;
517 }
518
519 m_telnetServer->newLineCallBack()(shared_from_this(), line) ? ++stats.successCmdCtr : ++stats.failCmdCtr;
520 addToHistory(line);
521 }
522
523 if (requirePromptReprint && m_telnetServer->interactivePrompt())
524 {
525 eraseLine();
527 }
528 }
529}
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)
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: