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

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

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

◆ closeClient()

void TelnetSession::closeClient ( )

Finish the session.

Definition at line 181 of file TelnetServer.cpp.

182{
183 spdlog::info("Telnet connection to {} closed", getPeerIP());
184
185 // Attempt to cleanly shutdown the connection since we're done
186 shutdown(m_socket, SHUT_WR);
187
188 // Cleanup
189 close(m_socket);
190
191 // Set disconnect time
192 stats.disconnectTime = std::chrono::high_resolution_clock::now();
193}
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 206 of file TelnetServer.cpp.

207{
208 // If you are an NVT command (i.e. first it of data is 255) then ignore the echo back
209 if (static_cast<uint8_t>(*buffer) == ASCII_NBSP)
210 {
211 return;
212 }
213
214 const ssize_t sendBytes = send(m_socket, buffer, length, 0);
215 if (sendBytes > 0)
216 {
217 stats.uploadBytes += static_cast<size_t>(sendBytes);
218 }
219}
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 143 of file TelnetServer.cpp.

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

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

◆ getPeerIP()

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

Definition at line 109 of file TelnetServer.cpp.

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

◆ initialise()

void TelnetSession::initialise ( )
protected

Initialise session.

Definition at line 221 of file TelnetServer.cpp.

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

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

◆ processBackspace()

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

Definition at line 300 of file TelnetServer.cpp.

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

◆ processCommandHistory()

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

Definition at line 378 of file TelnetServer.cpp.

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

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

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

◆ sendPromptAndBuffer()

void TelnetSession::sendPromptAndBuffer ( )
private

Definition at line 122 of file TelnetServer.cpp.

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

◆ stripEscapeCharacters()

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

Definition at line 282 of file TelnetServer.cpp.

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

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

◆ update()

void TelnetSession::update ( )
protected

Called every frame/loop by the Terminal Server.

Definition at line 447 of file TelnetServer.cpp.

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