summaryrefslogtreecommitdiffstats
path: root/net/base/telnet_server.cc
blob: 326095182706397430067373396eea292007328b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// 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/socket.h>
#include "base/message_loop.h"
#include "net/base/net_errors.h"
#include "third_party/libevent/event.h"
#include "base/message_pump_libevent.h"
#endif

#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 = 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);
}