From d5c33239e56f9ea2bec69979c378d066d4f9bcf6 Mon Sep 17 00:00:00 2001 From: KingToolbox Date: Mon, 24 Aug 2020 15:11:57 +0800 Subject: [PATCH] Add an implementation of Telnet protocol --- src/Protocols/Protocol.h | 36 ++++++ src/Protocols/TelnetProtocol.cpp | 93 ++++++++++++++ src/Protocols/TelnetProtocol.h | 206 +++++++++++++++++++++++++++++++ src/README.md | 8 +- 4 files changed, 341 insertions(+), 2 deletions(-) create mode 100644 src/Protocols/Protocol.h create mode 100644 src/Protocols/TelnetProtocol.cpp create mode 100644 src/Protocols/TelnetProtocol.h diff --git a/src/Protocols/Protocol.h b/src/Protocols/Protocol.h new file mode 100644 index 0000000..a889a2b --- /dev/null +++ b/src/Protocols/Protocol.h @@ -0,0 +1,36 @@ + /* + * Copyright 2020, WindTerm. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#pragma once + +#include + +class Protocol +{ +public: + Protocol(void) = default; + virtual ~Protocol(void) = default; + + QByteArray name() const { return m_name; } + +protected: + QByteArray m_name; +}; + +#endif // PROTOCOL_H \ No newline at end of file diff --git a/src/Protocols/TelnetProtocol.cpp b/src/Protocols/TelnetProtocol.cpp new file mode 100644 index 0000000..0424d81 --- /dev/null +++ b/src/Protocols/TelnetProtocol.cpp @@ -0,0 +1,93 @@ + /* + * Copyright 2020, WindTerm. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TelnetProtocol.h" + +#include +#include +#include + +const char *arrTelnetCmds[] = +{ + "XEOF", "SUSP", "ABORT", "EOR", "SE", + "NOP", "DM", "BREAK", "IP", "AO", + "AYT", "EC", "EL", "GA", "SB", + "WILL", "WONT", "DO", "DONT", "IAC" +}; + +const char *arrTelnetOpts[] = +{ + "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME", "STATUS", "TIMING MARK", "RCTE", + "NAOL", "NAOP", "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD", + "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO", "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT", "SEND LOCATION", + "TERMINAL TYPE", "END OF RECORD", "TACACS UID", "OUTPUT MARKING", "TTYLOC", "3270 REGIME", "X.3 PAD", "NAWS", + "TSPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW ENVIRON", + "TN3270E", "XAUTH", "CHARSET", "RSP", "COM PORT CONTROL", "SUPPRESS LOCAL ECHO", "START TLS", "KERMIT", + "SEND-URL", "FORWARD_X", +}; + +std::string TelnetProtocol::GetCommandName(uchar chTelnetCmd) +{ + if (chTelnetCmd >= TELCMD_XEOF && chTelnetCmd <= TELCMD_IAC) + return arrTelnetCmds[chTelnetCmd - TELCMD_XEOF]; + + std::ostringstream oss; + oss << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)chTelnetCmd << "(Unknown Telnet Command)"; + return oss.str(); +} + +std::string TelnetProtocol::GetOptionName(uchar chTelnetOption) +{ + if (chTelnetOption >= TELOPT_BINARY && chTelnetOption <= TELOPT_FORWARD_X) { return arrTelnetOpts[chTelnetOption - TELOPT_BINARY];} + else if (chTelnetOption == TELOPT_MCCP1) { return "MUD COMPRESSION PROTOCOL (V1)"; } + else if (chTelnetOption == TELOPT_MCCP2) { return "MUD COMPRESSION PROTOCOL (V2)"; } + else if (chTelnetOption == TELOPT_MSP) { return "MUD SOUND PROTOCOL"; } + else if (chTelnetOption == TELOPT_MXP) { return "MUD EXTENSION PROTOCOL";} + else if (chTelnetOption == TELOPT_PRAGMA_LOGON) { return "TELOPT PRAGMA LOGON"; } + else if (chTelnetOption == TELOPT_SSPI_LOGON) { return "TELOPT SSPI LOGON"; } + else if (chTelnetOption == TELOPT_PRAGMA_HEARTBEAT) { return "TELOPT PRAGMA HEARTBEAT"; } + else if (chTelnetOption == TELOPT_EXOPL) { return "EXTENDED OPTIONS LIST"; } + + std::ostringstream oss; + oss << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)chTelnetOption << "(Unknown Telnet Option)"; + return oss.str(); +} + +TelnetOptionVector &TelnetProtocol::GetOptionVector() +{ + static TelnetOptionVector m_vTelnetOption; + static std::once_flag flag; + + std::call_once(flag, [&]() { + m_vTelnetOption.clear(); + m_vTelnetOption.resize(TELOPT_INDEX_MAX); + + m_vTelnetOption[TELOPT_INDEX_NAWS].Init(TELOPT_NAWS, TELNET_CLIENT, TELOPT_STATE_INACTIVE_REQUESTED); + m_vTelnetOption[TELOPT_INDEX_TTYPE].Init(TELOPT_TTYPE, TELNET_CLIENT, TELOPT_STATE_INACTIVE_REQUESTED); + m_vTelnetOption[TELOPT_INDEX_ECHO].Init(TELOPT_ECHO, TELNET_SERVER, TELOPT_STATE_INACTIVE_REQUESTED); + m_vTelnetOption[TELOPT_INDEX_SERVER_SGA].Init(TELOPT_SGA, TELNET_SERVER, TELOPT_STATE_INACTIVE_REQUESTED); + m_vTelnetOption[TELOPT_INDEX_CLIENT_SGA].Init(TELOPT_SGA, TELNET_CLIENT, TELOPT_STATE_INACTIVE_REQUESTED); + m_vTelnetOption[TELOPT_INDEX_SERVER_BIN].Init(TELOPT_BINARY, TELNET_SERVER, TELOPT_STATE_INACTIVE); + m_vTelnetOption[TELOPT_INDEX_CLIENT_BIN].Init(TELOPT_BINARY, TELNET_CLIENT, TELOPT_STATE_INACTIVE); + }); + + return m_vTelnetOption; +} + +TelnetProtocol::TelnetProtocol() +{ + m_name = "Telnet"; +} diff --git a/src/Protocols/TelnetProtocol.h b/src/Protocols/TelnetProtocol.h new file mode 100644 index 0000000..5e91983 --- /dev/null +++ b/src/Protocols/TelnetProtocol.h @@ -0,0 +1,206 @@ + /* + * Copyright 2020, WindTerm. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TELNETPROTOCOL_H +#define TELNETPROTOCOL_H + +#pragma once + +#include +#include "Protocol.h" + +typedef unsigned char uchar; + +/* TELNET Command Codes: */ +constexpr uchar TELCMD_XEOF = 236; /* End of file: EOF is already used */ +constexpr uchar TELCMD_SUSP = 237; /* Suspend process */ +constexpr uchar TELCMD_ABORT = 238; /* Abort process */ +constexpr uchar TELCMD_EOR = 239; /* end of record (transparent mode) */ +constexpr uchar TELCMD_SE = 240; /* end sub negotiation */ +constexpr uchar TELCMD_NOP = 241; /* nop */ +constexpr uchar TELCMD_DM = 242; /* data mark--for connect. cleaning */ +constexpr uchar TELCMD_BREAK = 243; /* break */ +constexpr uchar TELCMD_IP = 244; /* interrupt process--permanently */ +constexpr uchar TELCMD_AO = 245; /* abort output--but let prog finish*/ +constexpr uchar TELCMD_AYT = 246; /* are you there */ +constexpr uchar TELCMD_EC = 247; /* erase the current character */ +constexpr uchar TELCMD_EL = 248; /* erase the current line */ +constexpr uchar TELCMD_GA = 249; /* you may reverse the line */ +constexpr uchar TELCMD_SB = 250; /* interpret as subnegotiation */ +constexpr uchar TELCMD_WILL = 251; /* I will use option */ +constexpr uchar TELCMD_WONT = 252; /* I won't use option */ +constexpr uchar TELCMD_DO = 253; /* please, you use option */ +constexpr uchar TELCMD_DONT = 254; /* you are not to use option */ +constexpr uchar TELCMD_IAC = 255; /* interpret as command */ + +/* The telnet options represented as strings */ +constexpr uchar TELOPT_BINARY = 0; // Binary Transmission - RFC 856 +constexpr uchar TELOPT_ECHO = 1; // Echo - RFC 857 +constexpr uchar TELOPT_RCP = 2; // Reconnection +constexpr uchar TELOPT_SGA = 3; // Suppress Go Ahead - RFC 858 +constexpr uchar TELOPT_NAMS = 4; // Approx Message Size Negotiation +constexpr uchar TELOPT_STATUS = 5; // Status - RFC 859 +constexpr uchar TELOPT_TM = 6; // Timing Mark - RFC 860 +constexpr uchar TELOPT_RCTE = 7; // Remote controlled transmission and echo - RFC 563,726 +constexpr uchar TELOPT_NAOL = 8; // Negotiate about output line width - NIC50005 +constexpr uchar TELOPT_NAOP = 9; // Negotiate about output page size - NIC50005 +constexpr uchar TELOPT_NAOCRD = 10; // Negotiate about CR disposition - RFC 652 +constexpr uchar TELOPT_NAOHTS = 11; // Negotiate about horizontal tabstops - RFC 653 +constexpr uchar TELOPT_NAOHTD = 12; // Negotiate about horizontal tab disposition - RFC 654 +constexpr uchar TELOPT_NAOFFD = 13; // Negotiate about formfeed disposition - RFC 655 +constexpr uchar TELOPT_NAOVTS = 14; // Negotiate about vertical tab stops - RFC 656 +constexpr uchar TELOPT_NAOVTD = 15; // Negotiate about vertical tab disposition - RFC 657 +constexpr uchar TELOPT_NAOLFD = 16; // Negotiate about output LF disposition - RFC 658 +constexpr uchar TELOPT_XASCII = 17; // Extended ascic character set - RFC 698 +constexpr uchar TELOPT_LOGOUT = 18; // Force logout - RFC 727 +constexpr uchar TELOPT_BM = 19; // Byte Macro - RFC 735 +constexpr uchar TELOPT_DET = 20; // Data Entry Terminal - RFC 732,1043 +constexpr uchar TELOPT_SUPDUP = 21; // SUPDUP Protocol - RFC 734,736 +constexpr uchar TELOPT_SUPDUPOUTPUT = 22; // SUPDUP Output - RFC 749 +constexpr uchar TELOPT_SNDLOC = 23; // Send Location - RFC 779 +constexpr uchar TELOPT_TTYPE = 24; // Terminal Type - RFC 1091 +constexpr uchar TELOPT_EOR = 25; // End of Record - RFC 885 +constexpr uchar TELOPT_TUID = 26; // TACACS User Identification - RFC 927 +constexpr uchar TELOPT_OUTMRK = 27; // Output Marking - RFC 933 +constexpr uchar TELOPT_TTYLOC = 28; // Terminal Location Number - RFC 946 +constexpr uchar TELOPT_3270REGIME = 29; // Telnet 3270 Regime - RFC 1041 +constexpr uchar TELOPT_X3PAD = 30; // X.3 PAD - RFC 1053 +constexpr uchar TELOPT_NAWS = 31; // Negotiate window size - RFC 1073 +constexpr uchar TELOPT_TSPEED = 32; // Terminal Speed - RFC 1079 +constexpr uchar TELOPT_LFLOW = 33; // Remote Flow Control - RFC 1372 +constexpr uchar TELOPT_LINEMODE = 34; // Linemode option - RFC 1184 +constexpr uchar TELOPT_XDISPLOC = 35; // X Display Location - RFC 1096 +constexpr uchar TELOPT_OLD_ENVIRON = 36; // Environment Option - RFC 1408 +constexpr uchar TELOPT_AUTHENTICATION = 37; // Authenticate - RFC 1416,2941,2942,2943,2951 +constexpr uchar TELOPT_ENCRYPT = 38; // Encryption Option - RFC 2946 +constexpr uchar TELOPT_NEW_ENVIRON = 39; // New Environment Option - RFC 1572 +constexpr uchar TELOPT_TN3270E = 40; // TN3270 enhancements - RFC 2355 +constexpr uchar TELOPT_XAUTH = 41; // XAUTH +constexpr uchar TELOPT_CHARSET = 42; // Negotiate charset to use - RFC 2066 +constexpr uchar TELOPT_RSP = 43; // Telnet remote serial port +constexpr uchar TELOPT_COM_PORT_OPTION = 44; // Com port control option - RFC 2217 +constexpr uchar TELOPT_SLE = 45; // Telnet suppress local echo +constexpr uchar TELOPT_STARTTLS = 46; // Telnet Start TLS +constexpr uchar TELOPT_KERMIT = 47; // Automatic Kermit file transfer - RFC 2840 +constexpr uchar TELOPT_SEND_URL = 48; // Send URL +constexpr uchar TELOPT_FORWARD_X = 49; // X forwarding +constexpr uchar TELOPT_MCCP1 = 85; // Mud Compression Protocol (v1) +constexpr uchar TELOPT_MCCP2 = 86; // Mud Compression Protocol (v2) +constexpr uchar TELOPT_MSP = 90; // Mud Sound Protocol +constexpr uchar TELOPT_MXP = 91; // Mud eXtension Protocol +constexpr uchar TELOPT_ZMP = 93; // Zenith Mud Protocol +constexpr uchar TELOPT_PRAGMA_LOGON = 138; // Telnet option pragma logon +constexpr uchar TELOPT_SSPI_LOGON = 139; // Telnet option SSPI login +constexpr uchar TELOPT_PRAGMA_HEARTBEAT = 140; // Telnet option pragma heartbeat +constexpr uchar TELOPT_GMCP = 201; // Generic Mud Communication Protocol +constexpr uchar TELOPT_EXOPL = 255; // extended-options-list - RFC 861 + +/* Option Subnegotiation Constants: */ +constexpr uchar TELSUB_IS = 0; // An option IS +constexpr uchar TELSUB_SEND = 1; // Send an option +constexpr uchar TELSUB_INFO = 2; // Environ: informational version of IS +constexpr uchar TELSUB_NAME = 3; + +/* Keyboard Command Characters: */ +constexpr char TELKEY_LF = '\n'; // Line Feed +constexpr char TELKEY_CR = '\r'; // Carriage Return +constexpr char TELKEY_BEL = '\a'; // Bell (attention signal) +constexpr char TELKEY_BS = '\b'; // Back Space +constexpr char TELKEY_HT = '\t'; // Horizontal Tab +constexpr char TELKEY_VT = '\v'; // Vertical Tab +constexpr char TELKEY_FF = '\f'; // Form Feed + +enum TelnetOptionIndex : uchar { + TELOPT_INDEX_NAWS, + TELOPT_INDEX_TSPEED, + TELOPT_INDEX_TTYPE, + TELOPT_INDEX_NEW_ENVIRON, + TELOPT_INDEX_LFLOW, + TELOPT_INDEX_LINEMODE, + TELOPT_INDEX_ECHO, + TELOPT_INDEX_SERVER_SGA, + TELOPT_INDEX_CLIENT_SGA, + TELOPT_INDEX_SERVER_BIN, + TELOPT_INDEX_CLIENT_BIN, + TELOPT_INDEX_MAX +}; + +enum TelnetAction : uchar { + TELACT_ABORT, // Abort Process + TELACT_AO, // Abort Output + TELACT_AYT, // Are You There + TELACT_BREAK, // serial-line break + TELACT_EC, // Erase Character + TELACT_EL, // Erase Line + TELACT_EOF, // end-of-file on session input + TELACT_EOL, // Telnet end-of-line sequence (CRLF, as opposed to CR NUL that escapes a literal CR) + TELACT_EOR, // End Of Record + TELACT_GA, // Go Ahead + TELACT_IP, // Interrupt Process + TELACT_NOP, // transmit data with no effect + TELACT_SUSP // Suspend Process +}; + +enum TelnetSide : uchar { + TELNET_SERVER, + TELNET_CLIENT +}; + +#define BIT_INACTIVE 0x00 +#define BIT_ACTIVE 0x01 +#define BIT_REQUESTED 0x10 + +enum TelnetOptionState { + TELOPT_STATE_INACTIVE = BIT_INACTIVE, + TELOPT_STATE_ACTIVE = BIT_ACTIVE, + TELOPT_STATE_INACTIVE_REQUESTED = BIT_INACTIVE | BIT_REQUESTED, + TELOPT_STATE_ACTIVE_REQUESTED = BIT_ACTIVE | BIT_REQUESTED +}; + +struct TelnetOption { + int nTelnetOption; + + TelnetSide eTelnetSide; + TelnetOptionState eOptionState; + + TelnetOption() : + nTelnetOption(-1), + eTelnetSide(TELNET_SERVER), + eOptionState(TELOPT_STATE_INACTIVE) + {} + + void Init(uchar _nTelnetOption, TelnetSide _eTelnetSide, TelnetOptionState _eOptionState) + { + nTelnetOption = _nTelnetOption; + eTelnetSide = _eTelnetSide; + eOptionState = _eOptionState; + } +}; +typedef std::vector TelnetOptionVector; +typedef std::vector TelnetOptionStateVector; + +class TelnetProtocol : public Protocol +{ +public: + TelnetProtocol(); + + std::string GetCommandName(uchar chTelnetCmd); + std::string GetOptionName(uchar chTelnetOpt); + + TelnetOptionVector &GetOptionVector(); +}; + +#endif // TELNETPROTOCOL_H \ No newline at end of file diff --git a/src/README.md b/src/README.md index fe87f93..b97b10f 100644 --- a/src/README.md +++ b/src/README.md @@ -18,7 +18,7 @@ An improved version based on Onigmo 5.13.5. In particular, **the addition of ite ## Pty -An improved version based on ptyqt[https://github.com/kafeg/ptyqt]. **Almost all the code was rewritten to make the pty more robust and stable.** +An improved version based on [ptyqt](https://github.com/kafeg/ptyqt). **Almost all the code was rewritten to make the pty more robust and stable.** ## ScopeGuard.h @@ -26,4 +26,8 @@ A class of which the sole purpose is to run the function f in its destructor. Th ## Spin.h -A high-performance spin mutex and locker. \ No newline at end of file +A high-performance spin mutex and locker. + +## Protocol/TelentProtocol.h/cpp + +An implementation of Telnet protocol.