diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 22:42:52 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 22:42:52 +0000 |
commit | 586acc5fe142f498261f52c66862fa417c3d52d2 (patch) | |
tree | c98b3417a883f2477029c8cd5888f4078681e24e /net/base/telnet_server.cc | |
parent | a814a8d55429605fe6d7045045cd25b6bf624580 (diff) | |
download | chromium_src-586acc5fe142f498261f52c66862fa417c3d52d2.zip chromium_src-586acc5fe142f498261f52c66862fa417c3d52d2.tar.gz chromium_src-586acc5fe142f498261f52c66862fa417c3d52d2.tar.bz2 |
Add net to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base/telnet_server.cc')
-rw-r--r-- | net/base/telnet_server.cc | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/net/base/telnet_server.cc b/net/base/telnet_server.cc new file mode 100644 index 0000000..75d9361 --- /dev/null +++ b/net/base/telnet_server.cc @@ -0,0 +1,275 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// winsock2.h must be included first in order to ensure it is included before +// windows.h. +#include <winsock2.h> + +#include "net/base/telnet_server.h" + +#define READ_BUF_SIZE 200 + +// Telnet protocol constants. +class TelnetProtocol { + public: + // Telnet command definitions (from arpa/telnet.h). + enum Commands { + IAC = 255, // Interpret as command. + DONT = 254, // You are not to use option. + DO = 253, // Please, you use option. + WONT = 252, // I won't use option. + WILL = 251, // I will use option. + SB = 250, // Interpret as subnegotiation. + GA = 249, // You may reverse the line. + EL = 248, // Erase the current line. + EC = 247, // Erase the current character. + AYT = 246, // Are you there. + AO = 245, // Abort output--but let prog finish. + IP = 244, // Interrupt process--permanently. + BREAK = 243, // Break. + DM = 242, // Data mark--for connect. cleaning. + NOP = 241, // Nop. + SE = 240, // End sub negotiation. + EOR = 239, // End of record (transparent mode). + ABORT = 238, // Abort process. + SUSP = 237, // Suspend process. + XEOF = 236 // End of file: EOF is already used... + }; + + // Telnet options (from arpa/telnet.h). + enum Options { + BINARY = 0, // 8-bit data path. + ECHO = 1, // Echo. + SGA = 3, // Suppress go ahead. + NAWS = 31, // Window size. + LFLOW = 33 // Remote flow control. + }; + + // Fixed character definitions mentioned in RFC 854. + enum Characters { + NUL = 0x00, + LF = 0x0A, + CR = 0x0D, + BELL = 0x07, + BS = 0x08, + HT = 0x09, + VT = 0x0B, + FF = 0x0C, + DEL = 0x7F, + ESC = 0x1B + }; +}; + + +/////////////////////// + +// must run in the IO thread +TelnetServer::TelnetServer(SOCKET s, ListenSocketDelegate* del, MessageLoop *l) + : ListenSocket(s, del, l) { + input_state_ = NOT_IN_IAC_OR_ESC_SEQUENCE; +} + +// must run in the IO thread +TelnetServer::~TelnetServer() { +} + +void TelnetServer::SendIAC(int command, int option) { + char data[3]; + data[0] = static_cast<unsigned char>(TelnetProtocol::IAC); + data[1] = static_cast<unsigned char>(command); + data[2] = option; + Send(data, 3); +} + +// always fixup \n to \r\n +void TelnetServer::SendInternal(const char* data, int len) { + int begin_index = 0; + for (int i = 0; i < len; i++) { + if (data[i] == TelnetProtocol::LF) { + // Send CR before LF if missing before. + if (i == 0 || data[i - 1] != TelnetProtocol::CR) { + // Send til before LF. + ListenSocket::SendInternal(data + begin_index, i - begin_index); + // Send CRLF. + ListenSocket::SendInternal("\r\n", 2); + // Continue after LF. + begin_index = i + 1; + } + } + } + // Send what is left (the whole string is sent here if CRLF was ok) + ListenSocket::SendInternal(data + begin_index, len - begin_index); +} + +void TelnetServer::Accept() { + SOCKET conn = ListenSocket::Accept(socket_); + if (conn == INVALID_SOCKET) { + // TODO + } else { + scoped_refptr<TelnetServer> sock = + new TelnetServer(conn, socket_delegate_, loop_); + + // Setup the way we want to communicate + sock->SendIAC(TelnetProtocol::DO, TelnetProtocol::ECHO); + sock->SendIAC(TelnetProtocol::DO, TelnetProtocol::NAWS); + sock->SendIAC(TelnetProtocol::DO, TelnetProtocol::LFLOW); + sock->SendIAC(TelnetProtocol::WILL, TelnetProtocol::ECHO); + sock->SendIAC(TelnetProtocol::WILL, TelnetProtocol::SGA); + + // it's up to the delegate to AddRef if it wants to keep it around + socket_delegate_->DidAccept(this, sock); + } +} + +TelnetServer* TelnetServer::Listen(std::string ip, int port, + ListenSocketDelegate *del, MessageLoop* l) { + SOCKET s = ListenSocket::Listen(ip, port); + if (s == INVALID_SOCKET) { + // TODO + } else { + TelnetServer *serv = new TelnetServer(s, del, l); + serv->Listen(); + return serv; + } + return NULL; +} + +void TelnetServer::StateMachineStep(unsigned char c) { + switch (input_state_) { + case NOT_IN_IAC_OR_ESC_SEQUENCE: + if (c == TelnetProtocol::IAC) { + // Expect IAC command + input_state_ = EXPECTING_COMMAND; + } else if (c == TelnetProtocol::ESC) { + // Expect left suare bracket + input_state_ = EXPECTING_FIRST_ESC_CHARACTER; + } else { + char data[1]; + data[0] = c; + // handle backspace specially + if (c == TelnetProtocol::DEL) { + if (!command_line_.empty()) { + command_line_.erase(--command_line_.end()); + Send(data, 1); + } + } else { + // Collect command + if (c >= ' ') + command_line_ += c; + // Echo character to client (for now ignore control characters). + if (c >= ' ' || c == TelnetProtocol::CR) { + Send(data, 1); + } + // Check for line termination + if (c == TelnetProtocol::CR) + input_state_ = EXPECTING_NEW_LINE; + } + } + break; + case EXPECTING_NEW_LINE: + if (c == TelnetProtocol::LF) { + Send("\n", 1); + socket_delegate_->DidRead(this, command_line_); + command_line_ = ""; + } + input_state_ = NOT_IN_IAC_OR_ESC_SEQUENCE; + break; + case EXPECTING_COMMAND: + // Read command, expect option. + iac_command_ = c; + input_state_ = EXPECTING_OPTION; + break; + case EXPECTING_OPTION: + // Read option + iac_option_ = c; + // check for subnegoating if not done reading IAC. + if (iac_command_ != TelnetProtocol::SB) { + input_state_ = NOT_IN_IAC_OR_ESC_SEQUENCE; + } else { + input_state_ = SUBNEGOTIATION_EXPECTING_IAC; + } + break; + case SUBNEGOTIATION_EXPECTING_IAC: + // Currently ignore content of subnegotiation. + if (c == TelnetProtocol::IAC) + input_state_ = SUBNEGOTIATION_EXPECTING_SE; + break; + case SUBNEGOTIATION_EXPECTING_SE: + // Character must be SE and subnegotiation is finished. + input_state_ = NOT_IN_IAC_OR_ESC_SEQUENCE; + break; + case EXPECTING_FIRST_ESC_CHARACTER: + if (c == '[') { + // Expect ESC sequence content. + input_state_ = EXPECTING_NUMBER_SEMICOLON_OR_END; + } else if (c == 'O') { + // VT100 "ESC O" sequence. + input_state_ = EXPECTING_SECOND_ESC_CHARACTER; + } else { + // Unknown ESC sequence - ignore. + } + break; + case EXPECTING_SECOND_ESC_CHARACTER: + // Ignore ESC sequence content for now. + input_state_ = NOT_IN_IAC_OR_ESC_SEQUENCE; + break; + case EXPECTING_NUMBER_SEMICOLON_OR_END: + if (isdigit(c) || c ==';') { + // Ignore ESC sequence content for now. + } else { + // Final character in ESC sequence. + input_state_ = NOT_IN_IAC_OR_ESC_SEQUENCE; + } + break; + } +} + +void TelnetServer::Read() { + char buf[READ_BUF_SIZE]; + int len; + do { + len = recv(socket_, buf, READ_BUF_SIZE, 0); + if (len == SOCKET_ERROR) { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) { + break; + } else { + // TODO - error + break; + } + } else { + const char *data = buf; + for (int i = 0; i < len; ++i) { + unsigned char c = static_cast<unsigned char>(*data); + StateMachineStep(c); + data++; + } + } + } while (len == READ_BUF_SIZE); +} |