7#include <spdlog/spdlog.h>
42 {
"clear",
"Clears the terminal screen"},
43 {
"disable log",
"Resets logger level"},
44 {
"enable log", R
"(Enable specified logger level. Level can be "v" (info), "vv" (debug) and "vvv" (trace))"},
45 {"help",
"Prints available commands"},
46 {
"ping",
"Pings the server"},
47 {
"status",
"Checks the internal status"},
48 {
"version",
"Displays the current version"},
56 {
"quit",
"Ends the connection"}};
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);
115 std::array<char, INET_ADDRSTRLEN> ipAddr{};
116 inet_ntop(AF_INET, &client_info.sin_addr, ipAddr.data(), INET_ADDRSTRLEN);
118 return ipAddr.data();
152 const std::string moveBack =
"\x1b[80D";
153 sendBytes = send(
m_socket, moveBack.c_str(), moveBack.length(), 0);
169 if (
auto sendBytes = send(
m_socket, data.c_str(), data.length(), 0) > 0)
182 spdlog::info(
"Telnet connection to {} closed",
getPeerIP());
196 return (llabs(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() -
lastSeenTime)
202 lastSeenTime = std::chrono::system_clock::time_point(std::chrono::duration<int>(0));
208 if (
static_cast<uint8_t
>(*buffer) ==
ASCII_NBSP)
213 const ssize_t sendBytes = send(
m_socket, buffer, length, 0);
223 spdlog::info(
"Telnet connection received from {}",
getPeerIP());
228 unsigned long iMode = 1;
232 const std::array<uint8_t, 3> willEcho{0xff, 0xfb, 0x01};
233 ssize_t sendBytes = send(
m_socket, willEcho.data(), 3, 0);
240 const std::array<uint8_t, 3> dontEcho{0xff, 0xfe, 0x01};
241 sendBytes = send(
m_socket, dontEcho.data(), 3, 0);
248 const std::array<uint8_t, 3> willSGA{0xff, 0xfb, 0x03};
249 sendBytes = send(
m_socket, willSGA.data(), 3, 0);
269 found = buffer.find_first_of(
static_cast<char>(
ASCII_NBSP));
270 if (found != std::string::npos && (found + 2) <= buffer.length() - 1)
272 buffer.erase(found, 3);
274 else if ((found + 2) >= buffer.length())
278 }
while (found != std::string::npos);
286 for (
const auto &cursor : cursors)
290 found = buffer.find(cursor);
291 if (found != std::string::npos)
293 buffer.erase(found, cursor.length());
295 }
while (found != std::string::npos);
301 bool foundBackspaces =
false;
307 found = buffer.find_first_of(
'\x7f');
308 if (found == std::string::npos)
310 found = buffer.find_first_of(
'\b');
313 if (found != std::string::npos)
315 if (buffer.length() > 1 && (found > 0))
317 buffer.erase(found - 1, 2);
323 foundBackspaces =
true;
325 }
while (found != std::string::npos);
326 return foundBackspaces;
331 bool foundTabs =
false;
336 found = buffer.find_first_of(
'\t');
337 if (found != std::string::npos)
340 if (buffer.length() > 0)
342 buffer.erase(found, 1);
349 const std::string retCommand =
350 m_telnetServer->tabCallback()(shared_from_this(), buffer.substr(0, found));
351 if (!retCommand.empty())
353 buffer.erase(0, found);
354 buffer.insert(0, retCommand);
358 }
while (found != std::string::npos);
390 ssize_t sendBytes = 0;
407 ssize_t sendBytes = 0;
429 std::vector<std::string> lines;
434 found = buffer.find(
"\r\n");
435 if (found != std::string::npos)
437 lines.push_back(buffer.substr(0, found));
438 buffer.erase(0, found + 2);
440 }
while (found != std::string::npos);
447 ssize_t readBytes = 0;
448 std::array<char, DEFAULT_BUFLEN> recvbuf{};
460 if (readBytes < 0 && errno != EAGAIN)
464 else if (readBytes > 0)
472 echoBack(recvbuf.data(),
static_cast<size_t>(readBytes));
476 for (
size_t i = 0; i < static_cast<size_t>(readBytes); i++)
486 m_buffer.append(recvbuf.data(),
static_cast<unsigned int>(readBytes));
490 bool requirePromptReprint =
false;
496 requirePromptReprint =
true;
503 requirePromptReprint =
true;
508 requirePromptReprint =
true;
514 for (
const auto &line : lines)
546 catch (
const std::exception &e)
550 spdlog::error(
"Telnet server destructor thrown an exception: {}", e.what());
552 catch (
const std::exception &e2)
554 std::cerr <<
"Telnet server destructor and also logger thrown an exception: " << e.what() <<
'\n'
555 << e2.what() <<
'\n';
561 std::string promptString,
const std::shared_ptr<prometheus::Registry> ®,
562 const std::string &prependName)
574 memset(&hints, 0,
sizeof(hints));
576 hints.ai_family = AF_INET;
577 hints.ai_socktype = SOCK_STREAM;
578 hints.ai_protocol = IPPROTO_TCP;
579 hints.ai_flags = AI_PASSIVE;
582 addrinfo *result =
nullptr;
583 if (getaddrinfo(
nullptr, std::to_string(
m_listenPort).c_str(), &hints, &result) != 0)
589 m_listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
592 freeaddrinfo(result);
596 if (
int optionVal = 1; setsockopt(
m_listenSocket, SOL_SOCKET, SO_REUSEADDR, &optionVal,
sizeof(optionVal)) < 0)
598 freeaddrinfo(result);
604 if (bind(
m_listenSocket, result->ai_addr, result->ai_addrlen) < 0)
606 freeaddrinfo(result);
610 freeaddrinfo(result);
621 m_stats = std::make_unique<TelnetStats>(reg, listenPort, prependName);
641 const auto session = std::make_shared<TelnetSession>(ClientSocket, shared_from_this());
642 session->initialise();
644 session->sendLine(
"Too many active connections. Please try again later. \r\nClosing...");
645 session->closeClient();
649 const auto session = std::make_shared<TelnetSession>(ClientSocket, shared_from_this());
651 session->initialise();
657 spdlog::info(
"Telnet server started");
668 catch (
const std::exception &e)
670 spdlog::error(
"Telnet server failed: {}", e.what());
674 spdlog::info(
"Telnet server stopped");
687 bool newConnectionAccept =
false;
688 bool newConnectionRefused =
false;
694 if (select(
m_listenSocket + 1, &readSet,
nullptr,
nullptr, &timeout) > 0)
698 newConnectionAccept =
true;
702 newConnectionRefused =
true;
707 for (
size_t idx = 0; idx <
m_sessions.size(); ++idx)
738 m_stats->consumeStats(serverStats);
747 tSession->closeClient();
767 session->sendLine(
"");
768 session->sendLine(
"Available commands:");
769 session->sendLine(
"");
772 std::array<char, BUFSIZ> buffer{
'\0'};
773 if (snprintf(buffer.data(), BUFSIZ,
"%-25s : %s", command.c_str(), info.c_str()) > 0)
775 session->sendLine(buffer.data());
782 session->sendLine(
"\r\n"
783 "𝑲𝒆𝒆𝒑 𝒚𝒐𝒖𝒓 𝒆𝒚𝒆𝒔 𝒐𝒏 𝒕𝒉𝒆 𝒔𝒕𝒂𝒓𝒔 "
784 "𝒂𝒏𝒅 𝒚𝒐𝒖𝒓 𝒇𝒆𝒆𝒕 𝒐𝒏 𝒕𝒉𝒆 𝒈𝒓𝒐𝒖𝒏𝒅 "
791 spdlog::trace(
"Received message {}", line);
794 session->sendLine(line);
805 session->sendLine(
"OK");
811 session->sendLine(
"Default log mode enabled");
812 spdlog::set_level(spdlog::level::info);
815 session->sendLine(
"Disabling all logs");
816 spdlog::set_level(spdlog::level::off);
819 session->sendLine(
"Info log mode enabled");
820 spdlog::set_level(spdlog::level::info);
823 session->sendLine(
"Debug log mode enabled");
824 spdlog::set_level(spdlog::level::debug);
827 session->sendLine(
"Trace log mode enabled");
828 spdlog::set_level(spdlog::level::trace);
831 session->sendLine(
"pong");
834 session->sendLine(PROJECT_FULL_VERSION_STRING);
840 for (
const auto &[service, statusFlag] :
vCheckFlag)
842 std::ostringstream oss;
843 oss << std::left << std::setfill(
'.') << std::setw(
KEY_WIDTH) << service +
" " << std::setw(
VAL_WIDTH)
844 << std::right << (statusFlag->_M_i ?
" OK" :
" Not Active");
845 session->sendLine(oss.str());
856 session->sendLine(
"Closing connection");
857 session->sendLine(
"Goodbye!");
858 session->markTimeout();
861 session->sendLine(
"Unknown command received");
871 std::ostringstream sStream;
874 if (command.rfind(line, 0) == 0)
878 sStream << command << std::setw(
KEY_WIDTH);
882 if (ctr != 1 && (!sStream.str().empty()))
884 session->sendLine(sStream.str());
std::vector< std::pair< std::string, std::shared_ptr< std::atomic_flag > > > vCheckFlag
Global variable to check if the servers are running.
constexpr size_t constHasher(const char *s)
const std::string ANSI_UNDERLINE_OFF("\x1b[24m")
const std::string ANSI_FG_BLACK("\x1b[30m")
const std::string ANSI_ITALCIS_OFF("\x1b[23m")
const std::string ANSI_ITALICS_ON("\x1b[3m")
const std::string ANSI_BG_YELLOW("\x1b[43m")
const std::string ANSI_BG_BLUE("\x1b[44m")
const std::string TELNET_ERASE_LINE("\xff\xf8")
const std::string ANSI_FG_DEFAULT("\x1b[39m")
void TelnetConnectedCallback(const SP_TelnetSession &session)
const std::string ANSI_ARROW_RIGHT("\x1b\x5b\x43")
void TelnetPrintAvailableCommands(const SP_TelnetSession &session)
const std::string ANSI_FG_MAGENTA("\x1b[35m")
const std::string ANSI_INVERSE_OFF("\x1b[27m")
const std::string ANSI_ARROW_DOWN("\x1b\x5b\x42")
const std::string ANSI_STRIKETHROUGH_ON("\x1b[9m")
const std::string ANSI_ARROW_UP("\x1b\x5b\x41")
const std::string ANSI_FG_CYAN("\x1b[36m")
const std::string ANSI_UNDERLINE_ON("\x1b[4m")
const std::string ANSI_ERASE_SCREEN("\x1b[2J")
const std::string ANSI_FG_BLUE("\x1b[34m")
const std::string ANSI_DOUBLE_HORIZONTAL_TAB("\t\t")
const std::string ANSI_FG_RED("\x1b[31m")
constexpr int SLEEP_INTERVAL_MS
const std::string ANSI_FG_YELLOW("\x1b[33m")
const std::string ANSI_HORIZONTAL_TAB("\t")
const std::string ANSI_BG_RED("\x1b[41m")
const std::string ANSI_FG_GREEN("\x1b[32m")
const std::string ANSI_BOLD_ON("\x1b[1m")
const std::string ANSI_BG_WHITE("\x1b[47m")
const std::string ANSI_STRIKETHROUGH_OFF("\x1b[29m")
const std::string ANSI_BG_GREEN("\x1b[42m")
const std::string ANSI_BG_DEFAULT("\x1b[49m")
std::string TelnetTabCallback(const SP_TelnetSession &session, std::string_view line)
constexpr int DEFAULT_BUFLEN
constexpr int MAX_AVAILABLE_SESSION
constexpr int INVALID_SOCKET
const std::string TELNET_CLEAR_SCREEN("\033[2J")
const std::string ANSI_BG_CYAN("\x1b[46m")
const std::string ANSI_BG_MAGENTA("\x1b[45m")
const std::string ANSI_BOLD_OFF("\x1b[22m")
const std::string ANSI_ARROW_LEFT("\x1b\x5b\x44")
const std::string ANSI_BG_BLACK("\x1b[40m")
const std::vector< std::pair< std::string, std::string > > telnetCommands
constexpr int TELNET_TIMEOUT
constexpr int TELNET_HISTORY_LIMIT
const std::string ANSI_INVERSE_ON("\x1b[7m")
const std::string ANSI_FG_WHITE("\x1b[37m")
const std::string ANSI_ERASE_LINE("\x1b[2K")
bool TelnetMessageCallback(const SP_TelnetSession &session, const std::string &line)
std::shared_ptr< TelnetSession > SP_TelnetSession
std::unique_ptr< TelnetStats > m_stats
unsigned long m_listenPort
std::atomic_flag m_shouldStop
std::string m_promptString
std::unique_ptr< std::thread > m_serverThread
bool initialise(unsigned long listenPort, const std::shared_ptr< std::atomic_flag > &checkFlag, std::string promptString="", const std::shared_ptr< prometheus::Registry > ®=nullptr, const std::string &prependName="")
void shutdown()
Closes the Telnet Server.
VEC_SP_TelnetSession m_sessions
void threadFunc() noexcept
std::shared_ptr< std::atomic_flag > m_checkFlag
const std::string & promptString() const
~TelnetServer()
Destructor for server.
void update()
Process new connections and messages.
void update()
Called every frame/loop by the Terminal Server.
TelnetSessionStats stats
Statistics variables.
bool processTab(std::string &buffer)
void markTimeout()
Marks timeout to close session.
bool processCommandHistory(std::string &buffer)
std::chrono::system_clock::time_point lastSeenTime
static bool processBackspace(std::string &buffer)
static void stripEscapeCharacters(std::string &buffer)
static void stripNVT(std::string &buffer)
bool checkTimeout() const
Checks the connection timeout.
void sendLine(std::string data)
Send a line of data to the Telnet Server.
std::shared_ptr< TelnetServer > m_telnetServer
void sendPromptAndBuffer()
static std::vector< std::string > getCompleteLines(std::string &buffer)
void closeClient()
Finish the session.
std::list< std::string > m_history
void initialise()
Initialise session.
void echoBack(const char *buffer, unsigned long length)
void addToHistory(const std::string &line)
std::string getPeerIP() const
std::list< std::string >::iterator m_historyCursor
std::chrono::high_resolution_clock::time_point processingTimeStart
Processing time start.
uint64_t activeConnectionCtr
Number of active connections.
std::chrono::high_resolution_clock::time_point processingTimeEnd
Processing time end.
uint64_t refusedConnectionCtr
Number of refused connections.
uint64_t acceptedConnectionCtr
Number of accepted connections.
std::chrono::high_resolution_clock::time_point disconnectTime
Connection end time.
uint64_t failCmdCtr
Failed commands.
uint64_t successCmdCtr
Successful commands.
size_t downloadBytes
Downloaded bytes.
std::chrono::high_resolution_clock::time_point connectTime
Connection start time.
size_t uploadBytes
Uploaded bytes.