Telnet.cc
Go to the documentation of this file.
1 //
2 // Copyright (c) 2020 Fraunhofer Institute for Applied Information Technology (FIT)
3 // Network Research Group (NET)
4 // Schloss Birlinghoven, 53754 Sankt Augustin, GERMANY
5 // Contact: support@wiback.org
6 //
7 // This file is part of the SENF code tree.
8 // It is licensed under the 3-clause BSD License (aka New BSD License).
9 // See LICENSE.txt in the top level directory for details or visit
10 // https://opensource.org/licenses/BSD-3-Clause
11 //
12 
13 
17 #include "Telnet.hh"
18 //#include "Telnet.ih"
19 
20 // Custom includes
21 #include <boost/algorithm/string/case_conv.hpp>
23 #include <senf/Utils/membind.hh>
25 
26 //#include "Telnet.mpp"
27 #define prefix_
28 //-/////////////////////////////////////////////////////////////////////////////////////////////////
29 
31  : handle_ (handle), charState_ (NORMAL), command_ (CMD_NONE), option_ (0),
32  inputEvent_ ("senf::term::BaseTelnetProtocol::input",
33  membind(&BaseTelnetProtocol::readHandler, this), handle,
34  scheduler::FdEvent::EV_READ),
35  outputEvent_ ("senf::term::BaseTelnetProtocol::output",
36  membind(&BaseTelnetProtocol::writeHandler, this), handle,
37  scheduler::FdEvent::EV_WRITE, false),
38  pendingRequests_ (0u),
39  requestTimeout_ (ClockService::milliseconds(DEFAULT_REQUEST_TIMEOUT_MS)),
40  timeout_ ("senf::term::BaseTelnetProtocol::configTimeout",
41  membind(&BaseTelnetProtocol::timeout, this))
42 {}
43 
45  : handle_ (), charState_ (NORMAL), command_ (CMD_NONE), option_ (0),
46  inputEvent_ ("senf::term::BaseTelnetProtocol::input", 0),
47  outputEvent_ ("senf::term::BaseTelnetProtocol::output", 0),
48  pendingRequests_ (0u),
49  requestTimeout_ (ClockService::milliseconds(DEFAULT_REQUEST_TIMEOUT_MS)),
50  timeout_ ("senf::term::BaseTelnetProtocol::timeout", 0)
51 {
52  SENF_ASSERT( false,
53  "Missing BaseTelnetProtocol constructor call in derived class "
54  "(BaseTelnetProtocol is a VIRTUAL base and MUST be constructed explicitly "
55  "in the most derived class." );
56 }
57 
59 {
60  for (std::string::const_iterator i (s.begin()); i != s.end(); ++i)
61  write(*i);
62 }
63 
65 {
66  switch (c) {
67  case '\r':
68  transmit('\r');
69  transmit('\0');
70  break;
71  case '\n':
72  transmit('\r');
73  transmit('\n');
74  break;
75  case '\xff':
76  transmit('\xff');
77  transmit('\xff');
78  break;
79  default:
80  transmit(c);
81  break;
82  }
83 }
84 
85 prefix_ void
87  std::string const & data)
88 {
89  transmit(CMD_IAC);
90  transmit(CMD_SB);
91  transmit(option);
92  for (std::string::const_iterator i (data.begin()); i != data.end(); ++i)
93  if (*i == '\xff') {
94  transmit('\xff');
95  transmit('\xff');
96  }
97  else
98  transmit(*i);
99  transmit(CMD_IAC);
100  transmit(CMD_SE);
101 }
102 
104 {}
105 
107 {}
108 
110 {}
111 
113 {}
114 
116 {}
117 
119 {}
120 
122 {}
123 
125 {}
126 
127 prefix_ void senf::term::BaseTelnetProtocol::handleChar(char c)
128 {
129  switch (charState_) {
130  case NORMAL:
131  handleNormalChar(c);
132  break;
133  case IAC_SEEN:
134  handleCommand(static_cast<unsigned char>(c));
135  break;
136  case EXPECT_OPTION:
137  handleOption(c);
138  break;
139  case CR_SEEN:
140  handleCR(c);
141  break;
142  case SB_OPTION:
143  handleSBOption(c);
144  break;
145  case SB_DATA:
146  handleSBData(c);
147  break;
148  case SB_IAC_SEEN:
149  handleSBIAC(c);
150  break;
151  }
152 }
153 
154 prefix_ void senf::term::BaseTelnetProtocol::handleNormalChar(char c)
155 {
156  switch (c) {
157  case '\r':
158  charState_ = CR_SEEN;
159  break;
160  case '\xff':
161  charState_ = IAC_SEEN;
162  break;
163  default:
164  emit(c);
165  break;
166  }
167 }
168 
169 prefix_ void senf::term::BaseTelnetProtocol::handleCommand(char c)
170 {
171  switch (static_cast<unsigned char>(c)) {
172  case CMD_SE:
173  // Ignore spurious SE commands .. they should only occur while in subnegotiation mode
174  charState_ = NORMAL;
175  break;
176  case CMD_NOP:
177  case CMD_DM:
178  case CMD_BRK:
179  case CMD_IP:
180  case CMD_AO:
181  case CMD_AYT:
182  case CMD_EC:
183  case CMD_EL:
184  case CMD_GA:
185  command_ = Command(static_cast<unsigned char>(c));
186  processCommand();
187  charState_ = NORMAL;
188  break;
189  case CMD_SB:
190  command_ = CMD_SB;
191  charState_ = SB_OPTION;
192  break;
193  case CMD_WILL:
194  case CMD_WONT:
195  case CMD_DO:
196  case CMD_DONT:
197  command_ = Command(static_cast<unsigned char>(c));
198  charState_ = EXPECT_OPTION;
199  break;
200  case CMD_IAC:
201  charState_ = NORMAL;
202  emit(CMD_IAC);
203  break;
204  default:
205  emit(CMD_IAC);
206  charState_ = NORMAL;
207  handleChar(c);
208  break;
209  }
210 }
211 
212 prefix_ void senf::term::BaseTelnetProtocol::handleOption(char c)
213 {
214  option_ = c;
215  processCommand();
216  charState_ = NORMAL;
217 }
218 
219 prefix_ void senf::term::BaseTelnetProtocol::handleCR(char c)
220 {
221  switch (c) {
222  case '\0':
223  emit('\r');
224  charState_ = NORMAL;
225  break;
226  case '\n':
227  emit('\n');
228  charState_ = NORMAL;
229  break;
230  default:
231  emit('\r');
232  charState_ = NORMAL;
233  handleChar(c);
234  break;
235  }
236 }
237 
238 prefix_ void senf::term::BaseTelnetProtocol::handleSBOption(char c)
239 {
240  option_ = c;
241  charState_ = SB_DATA;
242  data_.clear();
243 }
244 
245 prefix_ void senf::term::BaseTelnetProtocol::handleSBData(char c)
246 {
247  if (c == '\xff')
248  charState_ = SB_IAC_SEEN;
249  else
250  data_.push_back(c);
251 }
252 
253 prefix_ void senf::term::BaseTelnetProtocol::handleSBIAC(char c)
254 {
255  switch (static_cast<unsigned char>(c)) {
256  case CMD_IAC:
257  data_.push_back(c);
258  charState_ = SB_DATA;
259  break;
260  case CMD_SE:
261  processCommand();
262  charState_ = NORMAL;
263  break;
264  default:
265  charState_ = IAC_SEEN;
266  handleChar(c);
267  break;
268  }
269 }
270 
271 prefix_ void senf::term::BaseTelnetProtocol::processCommand()
272 {
273  switch (command_) {
274  case CMD_NONE:
275  case CMD_SE:
276  case CMD_DM:
277  case CMD_IAC:
278  break;
279  case CMD_NOP:
280  v_handleNOP();
281  break;
282  case CMD_BRK:
283  v_handleBRK();
284  break;
285  case CMD_IP:
286  v_handleIP();
287  break;
288  case CMD_AO:
289  v_handleAO();
290  break;
291  case CMD_AYT:
292  v_handleAYT();
293  break;
294  case CMD_EC:
295  v_handleEC();
296  break;
297  case CMD_EL:
298  v_handleEL();
299  break;
300  case CMD_GA:
301  v_handleGA();
302  break;
303  case CMD_SB:
304  {
305  OptionHandlerMap::const_iterator i (handlers_.find(option_));
306  if (i != handlers_.end())
307  i->second->v_handleOptionParameters(data_);
308  break;
309  }
310  case CMD_WILL:
311  case CMD_WONT:
312  response(getOption(false, option_), command_ == CMD_WILL);
313  break;
314  case CMD_DO:
315  case CMD_DONT:
316  response(getOption(true, option_), command_ == CMD_DO);
317  break;
318  }
319 }
320 
321 prefix_ void senf::term::BaseTelnetProtocol::transmit(char c)
322 {
323  sendQueue_.push_back(c);
324  outputEvent_.enable();
325 }
326 
328 {
329  ++ pendingRequests_;
330  timeout_.timeout(scheduler::eventTime() + requestTimeout_);
331 }
332 
333 prefix_ void senf::term::BaseTelnetProtocol::readHandler(int state)
334 {
335  if (state != scheduler::FdEvent::EV_READ || handle_.eof()) {
336  inputEvent_.disable();
337  v_eof();
338  return;
339  }
340  std::string data;
341  handle_.read(data, 0u);
342  for (std::string::const_iterator i (data.begin()); i != data.end(); ++i)
343  handleChar(*i);
344 }
345 
346 prefix_ void senf::term::BaseTelnetProtocol::writeHandler(int state)
347 {
348  if (state != scheduler::FdEvent::EV_WRITE) {
349  outputEvent_.disable();
350  inputEvent_.disable();
351  v_eof();
352  return;
353  }
354  sendQueue_.erase(sendQueue_.begin(),
355  handle_.write(boost::make_iterator_range(
356  sendQueue_.begin(), sendQueue_.end())));
357  if (sendQueue_.empty())
358  outputEvent_.disable();
359 }
360 
361 prefix_ void senf::term::BaseTelnetProtocol::timeout()
362 {
363  if (pendingRequests_ > 0u) {
364  pendingRequests_ = 0u;
365  v_setupComplete();
366  }
367 }
368 
369 prefix_ senf::term::BaseTelnetProtocol::OptInfo &
370 senf::term::BaseTelnetProtocol::getOption(bool local, option_type option)
371 {
372  OptionsMap::iterator i (options_.find(std::make_pair(local, option)));
373  if (i == options_.end())
374  i = options_.insert(std::make_pair(std::make_pair(local, option),
375  OptInfo(local, option))).first;
376  return i->second;
377 }
378 
379 prefix_ void senf::term::BaseTelnetProtocol::request(OptInfo & info, bool enabled)
380 {
381  info.wantState = enabled ? OptInfo::WANTED : OptInfo::DISABLED;
382  if (enabled != info.enabled) {
383  transmit(CMD_IAC);
384  transmit((info.local ? CMD_WILL : CMD_DO) + (enabled ? 0 : 1));
385  transmit(info.option);
386  info.optionState = OptInfo::REQUEST_SENT;
388  }
389 }
390 
391 prefix_ void senf::term::BaseTelnetProtocol::response(OptInfo & info, bool enabled)
392 {
393  bool decrementCount (false);
394 
395  // If this is a response, we need to unconditionally accept it. If this is a remote
396  // configuration request, we accept it if wantState is wither WANTED or ACCEPTED. If this is a
397  // response, we never send out a reply. If it is a remote request we send out a reply only if
398  // either a) we reject the request or b) we accept it AND we have changed our own mode.
399  if (info.optionState == OptInfo::REQUEST_SENT) {
400  // This is a response
401  info.optionState = OptInfo::ACKNOWLEDGED;
402  info.enabled = enabled;
403  decrementCount = true;
404  }
405  else if (enabled != info.enabled) {
406  // Request to change the current state
407  if (!enabled ||
408  (enabled && (info.wantState == OptInfo::WANTED || info.wantState == OptInfo::ACCEPTED))) {
409  // accept the request
410  info.optionState = OptInfo::ACKNOWLEDGED;
411  info.enabled = enabled;
412  } // else reject the request
413  transmit(CMD_IAC);
414  transmit((info.local ? CMD_WILL : CMD_DO) + (info.enabled ? 0 : 1));
415  transmit(info.option);
416  }
417  else
418  return;
419  if (info.enabled) {
420  OptionHandlerMap::const_iterator i (handlers_.find(info.option));
421  if (i != handlers_.end())
422  i->second->v_init();
423  }
424  if (decrementCount)
425  // This call must be AFTER calling v_init since v_init might increment the request count
426  // and v_setupComplete() might be called prematurely.
428 }
429 
431 {
432  if (pendingRequests_ > 0u) {
433  -- pendingRequests_;
434  if (pendingRequests_ == 0u) {
435  timeout_.disable();
436  v_setupComplete();
437  }
438  }
439 }
440 
441 //-/////////////////////////////////////////////////////////////////////////////////////////////////
442 // senf::term::telnethandler::TerminalType
443 
445 {
446  registerHandler(this);
447 }
448 
450 {
452 }
453 
454 prefix_ void senf::term::telnethandler::TerminalType::
455 v_handleOptionParameters(std::string const & data)
456 {
457  if (data.size() <= 0)
458  return;
459  if (data[0] == '\x00') {
460  type_ = data.substr(1);
461  boost::algorithm::to_lower(type_);
463  }
464 }
465 
466 prefix_ void senf::term::telnethandler::TerminalType::v_init()
467 {
468  nextTerminalType();
470 }
471 
472 //-/////////////////////////////////////////////////////////////////////////////////////////////////
473 // senf::term::telnethandler::NAWS
474 
476  : width_ (0u), height_ (0u)
477 {
478  registerHandler(this);
479 }
480 
481 prefix_ void senf::term::telnethandler::NAWS::v_init()
482 {
484 }
485 
486 prefix_ void
487 senf::term::telnethandler::NAWS::v_handleOptionParameters(std::string const & data)
488 {
489  if (data.size() != 4)
490  return;
491  width_ = (static_cast<unsigned char>(data[0])<<8)+static_cast<unsigned char>(data[1]);
492  height_ = (static_cast<unsigned char>(data[2])<<8)+static_cast<unsigned char>(data[3]);
493  if (! requestsPending())
496 }
497 
498 //-/////////////////////////////////////////////////////////////////////////////////////////////////
499 #undef prefix_
500 //#include "Telnet.mpp"
501 
502 
503 // Local Variables:
504 // mode: c++
505 // fill-column: 100
506 // comment-column: 40
507 // c-file-style: "senf"
508 // indent-tabs-mode: nil
509 // ispell-local-dictionary: "american"
510 // compile-command: "scons -u test"
511 // End:
virtual void v_handleGA()
Called, when the peer sends a GoAhead.
Definition: Telnet.cc:124
void nextTerminalType()
Request another terminal type.
Definition: Telnet.cc:449
static unsigned const DEFAULT_REQUEST_TIMEOUT_MS
Definition: Telnet.hh:134
BaseTelnetProtocol()
Provided for TelnetHandler mixins only.
Definition: Telnet.cc:44
virtual void v_handleEC()
Called, when the peer sends an EraseCharacter.
Definition: Telnet.cc:118
bool eof() const
virtual void v_handleBRK()
Called, when the peer sends a BReaK.
Definition: Telnet.cc:106
void write(std::string const &s)
Send string to peer.
Definition: Telnet.cc:58
u8 data[SPECTRAL_HT20_NUM_BINS]
void incrementRequestCounter()
Increment request counter.
Definition: Telnet.cc:327
virtual void v_handleEL()
Called, when the peer sends an EraseLine.
Definition: Telnet.cc:121
void sendOptionParameters(option_type option, std::string const &data)
Send extended option parameter to peer.
Definition: Telnet.cc:86
boost::function< R(Args)> membind(R(T::*fn)(Args), T *ob)
virtual void v_windowSizeChanged()=0
Called, whenever window size changes.
void registerHandler(Handler *h, bool request=true)
Register a TelnetHandler.
std::string read(unsigned limit=0)
virtual void v_handleIP()
Called, when the peer sends an InterruptProcess.
Definition: Telnet.cc:109
void timeout(ClockService::clock_type const &timeout, bool initiallyEnabled=true)
bool requestsPending()
true, if there are pending requests
#define prefix_
Definition: Telnet.cc:27
Telnet protocol implementation.
Definition: Telnet.hh:131
virtual void v_eof()=0
Called on input EOF.
void decrementRequestCounter()
Decrement request counter.
Definition: Telnet.cc:430
#define SENF_ASSERT(x, comment)
unsigned char option_type
Type of telnet option numbers.
Definition: Telnet.hh:142
virtual void v_setupComplete()=0
Called, when no further requests are pending.
virtual void v_handleAYT()
Called, when the peer sends an AreYouThere.
Definition: Telnet.cc:115
virtual void v_handleNOP()
Called, when the peer sends a NOP.
Definition: Telnet.cc:103
BaseTelnetProtocol::option_type const TERMINAL_TYPE
Definition: Telnet.hh:351
Telnet public header.
boost::range_const_iterator< ForwardReadableRange const >::type write(ForwardReadableRange const &range)
virtual void v_handleAO()
Called, when the peer sends an AbortOutput.
Definition: Telnet.cc:112