From: g0dil Date: Fri, 19 Dec 2008 18:59:14 +0000 (+0000) Subject: Utils/Console: Telnet protocol implementation (including NAWS and TERMINAL-TYPE options) X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=1f9ac79c1c2dfbb7abe22e1c816ca43d6582102b;p=senf.git Utils/Console: Telnet protocol implementation (including NAWS and TERMINAL-TYPE options) git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1032 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Utils/Console/FancyReader.hh b/Utils/Console/FancyReader.hh new file mode 100644 index 0000000..dd799d2 --- /dev/null +++ b/Utils/Console/FancyReader.hh @@ -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 +// +// 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 + + +// 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/SConscript b/Utils/Console/SConscript index 31345a6..a231c94 100644 --- a/Utils/Console/SConscript +++ b/Utils/Console/SConscript @@ -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 index 0000000..bc4b515 --- /dev/null +++ b/Utils/Console/Telnet.cc @@ -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 +// +// 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(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(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(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(data[0])<<8)+static_cast(data[1]), + (static_cast(data[2])<<8)+static_cast(data[3])); +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ +//#include "Telnet.mpp" + + +// 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 index 0000000..178a559 --- /dev/null +++ b/Utils/Console/Telnet.cci @@ -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 +// +// 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_ + + +// 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 index 0000000..42b50a6 --- /dev/null +++ b/Utils/Console/Telnet.cti @@ -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 +// +// 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 +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_ + + +// 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 index 0000000..ee952b8 --- /dev/null +++ b/Utils/Console/Telnet.hh @@ -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 +// +// 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 +#include +#include +#include + +//#include "Telnet.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { +namespace console { +namespace detail { + + /** \brief Telnet server + + \see + RFC 855 Telnet option specifications + + \todo SYNCH handling + */ + class BaseTelnetProtocol + { + public: + typedef ClientSocketHandle::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 + 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, OptInfo> OptionsMap; + OptionsMap options_; + + typedef std::map OptionHandlerMap; + OptionHandlerMap handlers_; + + Handle handle_; + + typedef std::vector 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 + + +// 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 index 0000000..124897d --- /dev/null +++ b/Utils/Console/Telnet.test.cc @@ -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 +// +// 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 + +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +BOOST_AUTO_UNIT_TEST(telnet) +{} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ + + +// 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 index 0000000..d70a084 --- /dev/null +++ b/Utils/Console/telnetServer.cc @@ -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 +// +// 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" + + +// 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: