Utils/Console: Telnet protocol implementation (including NAWS and TERMINAL-TYPE options)
g0dil [Fri, 19 Dec 2008 18:59:14 +0000 (18:59 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1032 270642c3-0616-0410-b53a-bc976706d245

Utils/Console/FancyReader.hh [new file with mode: 0644]
Utils/Console/SConscript
Utils/Console/Telnet.cc [new file with mode: 0644]
Utils/Console/Telnet.cci [new file with mode: 0644]
Utils/Console/Telnet.cti [new file with mode: 0644]
Utils/Console/Telnet.hh [new file with mode: 0644]
Utils/Console/Telnet.test.cc [new file with mode: 0644]
Utils/Console/telnetServer.cc [new file with mode: 0644]

diff --git a/Utils/Console/FancyReader.hh b/Utils/Console/FancyReader.hh
new file mode 100644 (file)
index 0000000..dd799d2
--- /dev/null
@@ -0,0 +1,61 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief FancyReader public header */
+
+#ifndef HH_SENF_Scheduler_Console_FancyReader_
+#define HH_SENF_Scheduler_Console_FancyReader_ 1
+
+// Custom includes
+#include "Server.hh"
+
+//#include "FancyReader.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+namespace detail {
+
+    class FancyClientReader
+        : public ClientReader
+    {
+    };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "FancyReader.cci"
+//#include "FancyReader.ct"
+//#include "FancyReader.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
index 31345a6..a231c94 100644 (file)
@@ -5,9 +5,11 @@ import SENFSCons
 
 ###########################################################################
 
-sources, includes = SENFSCons.Glob(env, exclude=['testServer.cc'])
+sources, includes = SENFSCons.Glob(env, exclude=['testServer.cc', 'telnetServer.cc'])
 
 SENFSCons.StandardTargets(env)
 SENFSCons.Lib(env, sources)
 SENFSCons.Doxygen(env)
 SENFSCons.InstallIncludeFiles(env, includes)
+SENFSCons.Binary(env, "testServer", ['testServer.cc'])
+SENFSCons.Binary(env, "telnetServer", ['telnetServer.cc'])
diff --git a/Utils/Console/Telnet.cc b/Utils/Console/Telnet.cc
new file mode 100644 (file)
index 0000000..bc4b515
--- /dev/null
@@ -0,0 +1,467 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Telnet non-inline non-template implementation */
+
+#include "Telnet.hh"
+//#include "Telnet.ih"
+
+// Custom includes
+#include "../membind.hh"
+#include "../Logger/SenfLog.hh"
+
+//#include "Telnet.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+prefix_ senf::console::detail::BaseTelnetProtocol::BaseTelnetProtocol(Handle handle)
+    : handle_ (handle), charState_ (NORMAL), command_ (CMD_NONE), option_ (0),
+      inputEvent_ ("senf::console::detail::BaseTelnetProtocol::input",
+                   senf::membind(&BaseTelnetProtocol::readHandler, this), handle, 
+                   senf::scheduler::FdEvent::EV_READ),
+      outputEvent_ ("senf::console::detail::BaseTelnetProtocol::output",
+                    senf::membind(&BaseTelnetProtocol::writeHandler, this), handle,
+                    senf::scheduler::FdEvent::EV_WRITE, false)
+{}
+
+prefix_ senf::console::detail::BaseTelnetProtocol::BaseTelnetProtocol()
+    : handle_ (), charState_ (NORMAL), command_ (CMD_NONE), option_ (0),
+      inputEvent_ ("senf::console::detail::BaseTelnetProtocol::input", 0),
+      outputEvent_ ("senf::console::detail::BaseTelnetProtocol::output", 0)
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::write(std::string const & s)
+{
+    for (std::string::const_iterator i (s.begin()); i != s.end(); ++i)
+        write(*i);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::write(char c)
+{
+    switch (c) {
+    case '\r':
+        transmit('\r');
+        transmit('\0');
+        break;
+    case '\n':
+        transmit('\r');
+        transmit('\n');
+        break;
+    case '\xff':
+        transmit('\xff');
+        transmit('\xff');
+        break;
+    default:
+        transmit(c);
+        break;
+    }
+}
+
+prefix_ void
+senf::console::detail::BaseTelnetProtocol::sendOptionParameters(option_type option,
+                                                                std::string const & data)
+{
+    transmit(CMD_IAC);
+    transmit(CMD_SB);
+    transmit(option);
+    for (std::string::const_iterator i (data.begin()); i != data.end(); ++i)
+        if (*i == '\xff') {
+            transmit('\xff');
+            transmit('\xff');
+        }
+        else
+            transmit(*i);
+    transmit(CMD_IAC);
+    transmit(CMD_SE);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::v_handleNOP()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::v_handleBRK()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::v_handleIP()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::v_handleAO()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::v_handleAYT()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::v_handleEC()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::v_handleEL()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::v_handleGA()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::handleChar(char c)
+{
+    switch (charState_) {
+    case NORMAL:
+        handleNormalChar(c);
+        break;
+    case IAC_SEEN:
+        handleCommand(static_cast<unsigned char>(c));
+        break;
+    case EXPECT_OPTION:
+        handleOption(c);
+        break;
+    case CR_SEEN:
+        handleCR(c);
+        break;
+    case SB_OPTION:
+        handleSBOption(c);
+        break;
+    case SB_DATA:
+        handleSBData(c);
+        break;
+    case SB_IAC_SEEN:
+        handleSBIAC(c);
+        break;
+    }
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::handleNormalChar(char c)
+{
+    switch (c) {
+    case '\r':
+        charState_ = CR_SEEN;
+        break;
+    case '\xff':
+        charState_ = IAC_SEEN;
+        break;
+    default:
+        emit(c);
+        break;
+    }
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::handleCommand(char c)
+{
+    switch (c) {
+    case CMD_SE:
+        // Ignore spurious SE commands .. they should only occur while in subnegotiation mode
+        charState_ = NORMAL;
+        break;
+    case CMD_NOP:
+    case CMD_DM:
+    case CMD_BRK:
+    case CMD_IP:
+    case CMD_AO:
+    case CMD_AYT:
+    case CMD_EC:
+    case CMD_EL:
+    case CMD_GA:
+        command_ = Command(static_cast<unsigned char>(c));
+        processCommand();
+        charState_ = NORMAL;
+        break;
+    case CMD_SB:
+        command_ = CMD_SB;
+        charState_ = SB_OPTION;
+        break;
+    case CMD_WILL:
+    case CMD_WONT:
+    case CMD_DO:
+    case CMD_DONT:
+        command_ = Command(static_cast<unsigned char>(c));
+        charState_ = EXPECT_OPTION;
+        break;
+    case CMD_IAC:
+        charState_ = NORMAL;
+        emit(CMD_IAC);
+        break;
+    default:
+        emit(CMD_IAC);
+        charState_ = NORMAL;
+        handleChar(c);
+        break;
+    }
+}        
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::handleOption(char c)
+{
+    option_ = c;
+    processCommand();
+    charState_ = NORMAL;
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::handleCR(char c)
+{
+    switch (c) {
+    case '\0':
+        emit('\r');
+        charState_ = NORMAL;
+        break;
+    case '\n':
+        emit('\n');
+        charState_ = NORMAL;
+        break;
+    default:
+        emit('\r');
+        charState_ = NORMAL;
+        handleChar(c);
+        break;
+    }
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::handleSBOption(char c)
+{
+    option_ = c;
+    charState_ = SB_DATA;
+    data_.clear();
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::handleSBData(char c)
+{
+    if (c == '\xff')
+        charState_ = SB_IAC_SEEN;
+    else
+        data_.push_back(c);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::handleSBIAC(char c)
+{
+    switch (c) {
+    case CMD_IAC:
+        data_.push_back(c);
+        charState_ = SB_DATA;
+        break;
+    case CMD_SE:
+        processCommand();
+        charState_ = NORMAL;
+        break;
+    default:
+        charState_ = IAC_SEEN;
+        handleChar(c);
+        break;
+    }
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::processCommand()
+{
+    switch (command_) {
+    case CMD_NONE:
+    case CMD_SE:
+    case CMD_DM:
+    case CMD_IAC:
+        break;
+    case CMD_NOP:
+        v_handleNOP();
+        break;
+    case CMD_BRK:
+        v_handleBRK();
+        break;
+    case CMD_IP:
+        v_handleIP();
+        break;
+    case CMD_AO:
+        v_handleAO();
+        break;
+    case CMD_AYT:
+        v_handleAYT();
+        break;
+    case CMD_EC:
+        v_handleEC();
+        break;
+    case CMD_EL:
+        v_handleEL();
+        break;
+    case CMD_GA:
+        v_handleGA();
+        break;
+    case CMD_SB:
+    {
+        OptionHandlerMap::const_iterator i (handlers_.find(option_));
+        if (i != handlers_.end())
+            i->second->v_handleOptionParameters(data_);
+        break;
+    }
+    case CMD_WILL:
+    case CMD_WONT:
+        response(getOption(false, option_), command_ == CMD_WILL);
+        break;
+    case CMD_DO:
+    case CMD_DONT:
+        response(getOption(true, option_),  command_ == CMD_DO);
+        break;
+    }
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::transmit(char c)
+{
+    sendQueue_.push_back(c);
+    outputEvent_.enable();
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::readHandler(int state)
+{
+    if (state != senf::scheduler::FdEvent::EV_READ || handle_.eof()) {
+        inputEvent_.disable();
+        v_eof();
+        return;
+    }
+    std::string data;
+    handle_.read(data, 0u);
+    for (std::string::const_iterator i (data.begin()); i != data.end(); ++i)
+        handleChar(*i);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::writeHandler(int state)
+{ 
+    if (state != senf::scheduler::FdEvent::EV_WRITE) {
+        outputEvent_.disable();
+        inputEvent_.disable();
+        v_eof();
+        return;
+    }
+    sendQueue_.erase(sendQueue_.begin(), 
+                     handle_.write(std::make_pair(sendQueue_.begin(), sendQueue_.end())));
+    if (sendQueue_.empty())
+        outputEvent_.disable();
+}
+
+prefix_ senf::console::detail::BaseTelnetProtocol::OptInfo &
+senf::console::detail::BaseTelnetProtocol::getOption(bool local, option_type option)
+{
+    OptionsMap::iterator i (options_.find(std::make_pair(local, option)));
+    if (i == options_.end())
+        i = options_.insert(std::make_pair(std::make_pair(local, option),
+                                           OptInfo(local, option))).first;
+    return i->second;
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::request(OptInfo & info, bool enabled)
+{
+    info.wantState = enabled ? OptInfo::WANTED : OptInfo::DISABLED;
+    if (enabled != info.enabled) {
+        transmit(CMD_IAC);
+        transmit((info.local ? CMD_WILL : CMD_DO) + (enabled ? 0 : 1));
+        transmit(info.option);
+        info.optionState = OptInfo::REQUEST_SENT;
+    }
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::response(OptInfo & info, bool enabled)
+{
+    // If this is a response, we need to unconditionally accept it.  If this is a remote
+    // configuration request, we accept it if wantState is wither WANTED or ACCEPTED.  If this is a
+    // response, we never send out a reply. If it is a remote request we send out a reply only if
+    // either a) we reject the request or b) we accept it AND we have changed our own mode.
+    if (info.optionState == OptInfo::REQUEST_SENT) {
+        // This is a response
+        info.optionState = OptInfo::ACKNOWLEDGED;
+        info.enabled = enabled;
+    }
+    else if (enabled != info.enabled) {
+        // Request to change the current state
+        bool accept (enabled);
+        if (!enabled || 
+            enabled && (info.wantState == OptInfo::WANTED || info.wantState == OptInfo::ACCEPTED)) {
+            // Accept the request
+            info.optionState = OptInfo::ACKNOWLEDGED;
+            info.enabled = enabled;
+        }
+        else
+            // Reject the request
+            accept = info.enabled;
+        transmit(CMD_IAC);
+        transmit((info.local ? CMD_WILL : CMD_DO) + (accept ? 0 : 1));
+        transmit(info.option);
+    }
+    else
+        return;
+    if (info.enabled) {
+        OptionHandlerMap::const_iterator i (handlers_.find(info.option));
+        if (i != handlers_.end())
+            i->second->v_init();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::telnethandler::TerminalType
+
+prefix_ senf::console::detail::telnethandler::TerminalType::TerminalType()
+{
+    registerHandler(this);
+}
+
+prefix_ void senf::console::detail::telnethandler::TerminalType::nextTerminalType()
+{
+    sendOptionParameters(telnetopt::TERMINAL_TYPE, "\x01");
+}
+
+prefix_ void senf::console::detail::telnethandler::TerminalType::
+v_handleOptionParameters(std::string const & data)
+{
+    if (data.size() <= 0)
+        return;
+    if (data[0] == '\x00')
+        v_handleTerminalType(data.substr(1));
+}
+
+prefix_ void senf::console::detail::telnethandler::TerminalType::v_init()
+{
+    nextTerminalType();
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::telnethandler::NAWS
+
+prefix_ senf::console::detail::telnethandler::NAWS::NAWS()
+{
+    registerHandler(this);
+}
+
+prefix_ void senf::console::detail::telnethandler::NAWS::v_init()
+{}
+
+prefix_ void
+senf::console::detail::telnethandler::NAWS::v_handleOptionParameters(std::string const & data)
+{
+    if (data.size() != 4)
+        return;
+    v_handleWindowSize(
+        (static_cast<unsigned char>(data[0])<<8)+static_cast<unsigned char>(data[1]),
+        (static_cast<unsigned char>(data[2])<<8)+static_cast<unsigned char>(data[3]));
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "Telnet.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Utils/Console/Telnet.cci b/Utils/Console/Telnet.cci
new file mode 100644 (file)
index 0000000..178a559
--- /dev/null
@@ -0,0 +1,145 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Telnet inline non-template implementation */
+
+//#include "Telnet.ih"
+
+// Custom includes
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::BaseTelnetProtocol::OptInfo
+
+prefix_ senf::console::detail::BaseTelnetProtocol::OptInfo::OptInfo()
+    : local (false), option (0u), wantState (DISABLED), optionState (NONE), enabled (false)
+{}
+
+prefix_ senf::console::detail::BaseTelnetProtocol::OptInfo::OptInfo(bool l, option_type o)
+    : local (l), option (o), wantState (DISABLED), optionState (NONE), enabled (false)
+{}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::BaseTelnetProtocol::TelnetHandler
+
+prefix_ senf::console::detail::BaseTelnetProtocol::TelnetHandler::~TelnetHandler()
+{}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::BaseTelnetProtocol
+
+prefix_ senf::console::detail::BaseTelnetProtocol::~BaseTelnetProtocol()
+{}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::sendNOP()
+{
+    transmit(CMD_IAC);
+    transmit(CMD_NOP);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::sendBRK()
+{
+    transmit(CMD_IAC);
+    transmit(CMD_BRK);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::sendIP()
+{
+    transmit(CMD_IAC);
+    transmit(CMD_IP);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::sendAO()
+{
+    transmit(CMD_IAC);
+    transmit(CMD_AO);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::sendAYT()
+{
+    transmit(CMD_IAC);
+    transmit(CMD_AYT);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::sendEC()
+{
+    transmit(CMD_IAC);
+    transmit(CMD_EC);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::sendEL()
+{
+    transmit(CMD_IAC);
+    transmit(CMD_EL);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::sendGA()
+{
+    transmit(CMD_IAC);
+    transmit(CMD_GA);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::requestLocalOption(option_type option,
+                                                                           bool enabled)
+{
+    request(getOption(true, option), enabled);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::acceptLocalOption(option_type option,
+                                                                          bool enabled)
+{
+    getOption(true, option).wantState = OptInfo::ACCEPTED;
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::requestPeerOption(option_type option,
+                                                                          bool enabled)
+{
+    request(getOption(false, option), enabled);
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::acceptPeerOption(option_type option,
+                                                                         bool enabled)
+{
+    getOption(false, option).wantState = OptInfo::ACCEPTED;
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::emit(char c)
+{
+    v_charReceived(c);
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Utils/Console/Telnet.cti b/Utils/Console/Telnet.cti
new file mode 100644 (file)
index 0000000..42b50a6
--- /dev/null
@@ -0,0 +1,57 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Telnet inline template implementation */
+
+//#include "Telnet.ih"
+
+// Custom includes
+
+#define prefix_ inline
+///////////////////////////////cti.p///////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::BaseTelnetProtocol
+
+template <class Handler>
+prefix_ void senf::console::detail::BaseTelnetProtocol::registerHandler(Handler * h,
+                                                                        bool request)
+{
+    handlers_.insert(std::make_pair(Handler::OPTION_CODE, h));
+    if (request)
+        requestPeerOption(Handler::OPTION_CODE);
+}
+
+///////////////////////////////cti.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Utils/Console/Telnet.hh b/Utils/Console/Telnet.hh
new file mode 100644 (file)
index 0000000..ee952b8
--- /dev/null
@@ -0,0 +1,271 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Telnet public header */
+
+#ifndef HH_SENF_Scheduler_Console_Telnet_
+#define HH_SENF_Scheduler_Console_Telnet_ 1
+
+// Custom includes
+#include <vector>
+#include <map>
+#include <senf/Socket.hh>
+#include <senf/Scheduler/Scheduler.hh>
+
+//#include "Telnet.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+namespace detail {
+
+    /** \brief  Telnet server
+
+        \see 
+            <a href="http://tools.ietf.org/html/rfc854>RFC 854</a> The Telnet protocol \n
+            <a href="http://tools.ietf.org/html/rfc854>RFC 855</a> Telnet option specifications
+
+        \todo SYNCH handling
+     */
+    class BaseTelnetProtocol
+    {
+    public:
+        typedef ClientSocketHandle<senf::MakeSocketPolicy<
+            ConnectedCommunicationPolicy, 
+            StreamFramingPolicy, 
+            ReadablePolicy,
+            WriteablePolicy>::policy> Handle;
+
+        typedef unsigned char option_type;
+
+        struct TelnetHandler;
+
+        void write(std::string const & s);
+        void write(char c);
+
+        void sendNOP();
+        void sendBRK();
+        void sendIP();
+        void sendAO();
+        void sendAYT();
+        void sendEC();
+        void sendEL();
+        void sendGA();
+        
+        void sendOptionParameters(option_type option, std::string const & data);
+
+        void requestLocalOption(option_type option, bool enabled = true);
+        void acceptLocalOption(option_type option, bool enabled = true);
+
+        void requestPeerOption(option_type option, bool enabled = true);
+        void acceptPeerOption(option_type option, bool enabled = true);
+        
+    protected:
+        explicit BaseTelnetProtocol(Handle handle);
+        BaseTelnetProtocol();
+        virtual ~BaseTelnetProtocol();
+
+        template <class Handler>
+        void registerHandler(Handler * h, bool request=true);
+
+    private:
+
+#ifndef DOXYGEN
+    private:
+#endif
+        virtual void v_charReceived(char c) = 0;
+        virtual void v_eof() = 0;
+
+        virtual void v_handleNOP();
+        virtual void v_handleBRK();
+        virtual void v_handleIP();
+        virtual void v_handleAO();
+        virtual void v_handleAYT();
+        virtual void v_handleEC();
+        virtual void v_handleEL();
+        virtual void v_handleGA();
+        
+#ifdef DOXYGEN
+    private:
+#endif
+        void handleChar(char c);
+        void handleNormalChar(char c);
+        void handleCommand(char c);
+        void handleOption(char c);
+        void handleCR(char c);
+        void handleSBOption(char c);
+        void handleSBData(char c);
+        void handleSBIAC(char c);
+        void emit(char c);
+        void processCommand();
+        void transmit(char c);
+
+        void sendWILL(char option);
+        void sendWONT(char option);
+        void sendDO(char option);
+        void sendDONT(char option);
+
+        void readHandler(int state);
+        void writeHandler(int state);
+
+        enum Command { 
+            CMD_NONE = 0,
+            CMD_SE = 240,
+            CMD_NOP = 241,
+            CMD_DM = 242,
+            CMD_BRK = 243,
+            CMD_IP = 244,
+            CMD_AO = 245,
+            CMD_AYT = 246,
+            CMD_EC = 247,
+            CMD_EL = 248,
+            CMD_GA = 249,
+            CMD_SB = 250,
+            CMD_WILL = 251,
+            CMD_WONT = 252,
+            CMD_DO = 253,
+            CMD_DONT = 254,
+            CMD_IAC = 255,
+        };
+
+        struct OptInfo
+        {
+            enum WantState { WANTED, ACCEPTED, DISABLED };
+            enum OptionState { NONE, REQUEST_SENT, ACKNOWLEDGED };
+
+            OptInfo();
+            OptInfo(bool local, option_type option);
+
+            ///////////////////////////////////////////////////////////////
+
+            bool const local;
+            option_type const option;
+
+            WantState wantState;
+            OptionState optionState;
+            bool enabled;
+            
+        };
+
+        OptInfo & getOption(bool local, option_type option);
+        void request(OptInfo & info, bool enabled);
+        void response(OptInfo & info, bool enabled);
+
+        typedef std::map<std::pair<bool, option_type>, OptInfo> OptionsMap;
+        OptionsMap options_;
+
+        typedef std::map<option_type, TelnetHandler*> OptionHandlerMap;
+        OptionHandlerMap handlers_;
+
+        Handle handle_;
+
+        typedef std::vector<char> SendQueue;
+        SendQueue sendQueue_;
+
+        enum CharState { NORMAL, IAC_SEEN, EXPECT_OPTION, CR_SEEN, 
+                         SB_OPTION, SB_DATA, SB_IAC_SEEN };
+        CharState charState_;
+
+        Command command_;
+        option_type option_;
+        std::string data_;
+
+        senf::scheduler::FdEvent inputEvent_;
+        senf::scheduler::FdEvent outputEvent_;
+
+        friend class TelnetHandler;
+    };
+
+    struct BaseTelnetProtocol::TelnetHandler
+        : public virtual BaseTelnetProtocol
+    {
+        virtual ~TelnetHandler();
+        virtual void v_init() = 0;
+        virtual void v_handleOptionParameters(std::string const & data) = 0;
+    };
+
+    // See http://www.iana.org/assignments/telnet-options for a list of options
+    namespace telnetopt { BaseTelnetProtocol::option_type const ECHO = 1u; }
+    namespace telnetopt { BaseTelnetProtocol::option_type const TRANSMIT_BINARY = 0u; }
+    namespace telnetopt { BaseTelnetProtocol::option_type const SUPPRESS_GO_AHEAD = 3u; }
+    namespace telnetopt { BaseTelnetProtocol::option_type const TERMINAL_TYPE = 24u; }
+    namespace telnetopt { BaseTelnetProtocol::option_type const NAWS = 31u; }
+    namespace telnetopt { BaseTelnetProtocol::option_type const LINEMODE = 34u; }
+
+namespace telnethandler {
+
+    class TerminalType
+        : public BaseTelnetProtocol::TelnetHandler
+    {
+    public:
+        static option_type const OPTION_CODE = telnetopt::TERMINAL_TYPE;
+
+        void nextTerminalType();
+        
+    protected:
+        TerminalType();
+
+    private:
+        virtual void v_handleTerminalType(std::string const & type) = 0;
+
+        virtual void v_init();
+        virtual void v_handleOptionParameters(std::string const & data);
+    };
+
+    class NAWS 
+        : public BaseTelnetProtocol::TelnetHandler
+    {
+    public:
+        static option_type const OPTION_CODE = telnetopt::NAWS;
+
+    protected:
+        NAWS();
+        
+    private:
+        virtual void v_handleWindowSize(unsigned width, unsigned height) = 0;
+
+        virtual void v_init();
+        virtual void v_handleOptionParameters(std::string const & data);
+    };
+    
+}
+
+}}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+#include "Telnet.cci"
+
+//#include "Telnet.ct"
+#include "Telnet.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Utils/Console/Telnet.test.cc b/Utils/Console/Telnet.test.cc
new file mode 100644 (file)
index 0000000..124897d
--- /dev/null
@@ -0,0 +1,53 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Telnet.test unit tests */
+
+//#include "Telnet.test.hh"
+//#include "Telnet.test.ih"
+
+// Custom includes
+#include "Telnet.hh"
+
+#include "../../Utils/auto_unit_test.hh"
+#include <boost/test/test_tools.hpp>
+
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+BOOST_AUTO_UNIT_TEST(telnet)
+{}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Utils/Console/telnetServer.cc b/Utils/Console/telnetServer.cc
new file mode 100644 (file)
index 0000000..d70a084
--- /dev/null
@@ -0,0 +1,105 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief telnetServer non-inline non-template implementation */
+
+//#include "telnetServer.hh"
+//#include "telnetServer.ih"
+
+// Custom includes
+#include "Telnet.hh"
+#include "../Logger.hh"
+#include "../../Socket/Protocols/INet.hh"
+
+//#include "telnetServer.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+namespace {
+
+    class MyTelnet 
+        : public virtual senf::console::detail::BaseTelnetProtocol,
+          public senf::console::detail::telnethandler::TerminalType,
+          public senf::console::detail::telnethandler::NAWS
+    {
+    public:
+        explicit MyTelnet(Handle handle) : senf::console::detail::BaseTelnetProtocol(handle) 
+            {
+                requestPeerOption(senf::console::detail::telnetopt::SUPPRESS_GO_AHEAD);
+                requestLocalOption(senf::console::detail::telnetopt::SUPPRESS_GO_AHEAD);
+                requestLocalOption(senf::console::detail::telnetopt::ECHO);
+            }
+        
+    private:
+        virtual void v_charReceived(char c)
+            {
+                SENF_LOG(("Char: " << c));
+            }
+        virtual void v_eof()
+            {
+                SENF_LOG(("EOF"));
+                senf::scheduler::terminate();
+            }
+        virtual void v_handleTerminalType(std::string const & type)
+            {
+                SENF_LOG(("Terminal type: " << type));
+            }
+
+        virtual void v_handleWindowSize(unsigned width, unsigned height)
+            {
+                SENF_LOG(("Window size: " << width << "x" << height));
+            }
+    };
+
+    typedef senf::TCPv4ServerSocketHandle ServerHandle;
+    typedef ServerHandle::ClientHandle ClientHandle;
+    
+}
+
+int main(int argc, char const ** argv)
+{
+    SENF_LOG(("Starting server."));
+    ServerHandle server (ServerHandle::Address("127.0.0.1:22344"));
+    ClientHandle client (server.accept());
+    SENF_LOG(("Starting MyTelnet"));
+    MyTelnet telnet (client);
+
+    senf::scheduler::process();
+
+    return 0;
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "telnetServer.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u telnetServer"
+// End: