// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "build/build_config.h"
#if defined(OS_WIN)
// winsock2.h must be included first in order to ensure it is included before
// windows.h.
#include <winsock2.h>
#elif defined(OS_POSIX)
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "base/message_loop.h"
#include "net/base/net_errors.h"
#if defined(USE_SYSTEM_LIBEVENT)
#include <event.h>
#else
#include "third_party/libevent/event.h"
#endif
#include "base/message_pump_libevent.h"
#endif
#include "base/eintr_wrapper.h"
#include "net/base/telnet_server.h"
#if defined(OS_POSIX)
// Used same name as in Windows to avoid #ifdef where refrenced
#define SOCKET int
const int INVALID_SOCKET = -1;
const int SOCKET_ERROR = -1;
struct event; // From libevent
#endif
const int kReadBufSize = 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)
: ListenSocket(s, del) {
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_);
#if defined(OS_POSIX)
sock->WatchSocket(WAITING_READ);
#endif
// 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) {
SOCKET s = ListenSocket::Listen(ip, port);
if (s == INVALID_SOCKET) {
// TODO (ibrar): error handling
} else {
TelnetServer *serv = new TelnetServer(s, del);
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[kReadBufSize + 1];
int len;
do {
len = HANDLE_EINTR(recv(socket_, buf, kReadBufSize, 0));
#if defined(OS_WIN)
if (len == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err == WSAEWOULDBLOCK)
break;
#else
if (len == SOCKET_ERROR) {
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
#endif
} else if (len == 0) {
Close();
} 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 == kReadBufSize);
}