04100e20c8c050c35d06372c4bdc6fedb6cedfd0
[senf.git] / senf / Utils / Termlib / Telnet.hh
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Telnet public header */
25
26 #ifndef HH_SENF_Scheduler_Console_Telnet_
27 #define HH_SENF_Scheduler_Console_Telnet_ 1
28
29 // Custom includes
30 #include <vector>
31 #include <map>
32 #include <senf/Scheduler/Scheduler.hh>
33 #include <senf/Scheduler/ClockService.hh>
34
35 //#include "Telnet.mpp"
36 //-/////////////////////////////////////////////////////////////////////////////////////////////////
37
38 namespace senf {
39 namespace term {
40
41     /** \defgroup telnet_group Telnet protocol
42
43         The telnet protocol implementation is based on the relevant RFCs:
44
45         \li <a href="http://tools.ietf.org/html/rfc854">RFC 854</a> The Telnet protocol
46         \li <a href="http://tools.ietf.org/html/rfc854">RFC 855</a> Telnet option specifications
47
48         BaseTelnetProtocol provides the basic protocol implementation with generic telnet option
49         support. Several other classes provide additional support for further telnet options:
50
51         \li telnethandler::TerminalType provides support for the TERMINAL_TYPE option
52         \li telnethandler::NAWS provides support for the NAWS (Negotiation About Window Size) option
53
54         There are some symbolic constants for telnet option in the senf::term::telnetopt namespace.
55      */
56
57     /** \brief Telnet protocol implementation
58
59         This class implements the basic telnet protocol. It provides option parsing and negotiation
60         and implements the necessary character quoting.
61
62         This class is a base class. Several virtual function hooks are provided which will be called
63         by the protocol implementation.
64
65         The telnet protocol implementation is symmetric: At this level, there is no difference
66         between a telnet client and a telnet server.
67
68         This telnet protocol base class integrates with the SENF Scheduler, it relies on the
69         scheduler main loop to be active.
70
71         \section telnethandlers Telnet option handlers
72
73         To support complex telnet option which require subnegotiation, TelnetHandler's are
74         provided. A telnet handler is a class derived from TelnetHandler and then inherited into the
75         target telnet implementation class:
76
77         \code
78         class MyTelnet
79             : public senf::term::telnethandler::TerminalType,
80               public senf::term::telnethandler::NAWS
81         {
82             MyTelnet(Handle handle) : BaseTelnetProtocol(handle) {}
83
84             // ...
85         };
86         \endcode
87
88         It is very important, the BaseTelnetProtocol is \e always inherited by \c public \c virtual
89         inheritance. This allows the BaseTelnetProtocol constructor to be called by the most derived
90         class directly as above.
91
92         Each terminal handler may provide an additional API and/or additional virtual function
93         callbacks.
94
95         To implement a new terminal handler, derive from TelnetHandler and register the
96         handler. BaseTelnetProtocol will automatically request the corresponding option to be
97         enabled and will call the handlers \c v_init() member as soon as the peer as asserted the
98         option.
99
100         Whenever a subnegotiation for the registered handler is received, the handlers
101         \c v_handleOptionParameters() member is called.
102
103         \code
104         class MyTelnetHandler
105             : public senf::term::BaseTelnetProtocol::TelnetHandler
106         {
107         public:
108             // This constant is MANDATORY and gives the option code which this handler services
109             static option_type const OPTION_CODE = MY_OPTION_CODE;
110
111             void frobble() { // ... }
112
113         protected:
114             MyTelnetHandler() { registerHandler(this); }
115
116         private:
117             virtual void v_init()
118             {
119                 sendOptionParameters(OPTION_CODE, "my special subnegotiation");
120                 incrementRequestCounter();
121             }
122
123             virtual void v_handleOptionParameters(std::string const & data)
124             {
125                 if (data == "another special subnegotiation")
126                     decrementRequestCounter();
127             }
128         };
129         \endcode
130
131         \todo SYNCH handling
132
133         \ingroup telnet_group
134      */
135     class BaseTelnetProtocol
136     {
137     public:
138         static unsigned const DEFAULT_REQUEST_TIMEOUT_MS = 500u;
139
140         typedef ClientSocketHandle<senf::MakeSocketPolicy<
141             ConnectedCommunicationPolicy,
142             StreamFramingPolicy,
143             ReadablePolicy,
144             WriteablePolicy>::policy> Handle; ///< Type of socket handle required
145
146         typedef unsigned char option_type; ///< Type of telnet option numbers
147
148         struct TelnetHandler;
149
150         void write(std::string const & s); ///< Send string to peer
151                                         /**< The string will be correctly quoted and newline chars
152                                              will be sent as CR/LF pairs. */
153         void write(char c);             ///< Send single character to peer
154                                         /**< The character will be correctly quoted and newline
155                                              chars will be sent as CR/LF pairs. */
156
157         Handle handle();                ///< Get socket handle
158
159         void sendNOP();                 ///< Send NOP to peer
160         void sendBRK();                 ///< Send BReaK to peer
161         void sendIP();                  ///< Send InterruptProcess to peer
162         void sendAO();                  ///< Send AbortOutput to peer
163         void sendAYT();                 ///< Send AreYouThere to peer
164         void sendEC();                  ///< Send EraseCharacter to peer
165         void sendEL();                  ///< Send EraseLine to peer
166         void sendGA();                  ///< Send GoAhead to peer
167
168         void sendOptionParameters(option_type option, std::string const & data);
169                                         ///< Send extended option parameter to peer
170                                         /**< This will send \a data as extended option parameter of
171                                              option \a option to peer via a subnegotiation. */
172
173         void requestLocalOption(option_type option, bool enabled = true);
174                                         ///< Request option to be enabled here
175                                         /**< This will send a WILL \a option request to the peer */
176         void acceptLocalOption(option_type option, bool enabled = true);
177                                         ///< Accept a request for an option to be enabled here
178                                         /**< If the peer sends a DO \a option request, the request
179                                              will be granted */
180
181         void requestPeerOption(option_type option, bool enabled = true);
182                                         ///< Request peer to enable an option
183                                         /**< This will send a DO \a option request to the peer */
184         void acceptPeerOption(option_type option, bool enabled = true);
185                                         ///< Accept a request by the peer to enable an option
186                                         /**< If the peer sends a WILL \a option request, the request
187                                              will be ganted */
188
189         bool localOption(option_type option); ///< \c true, if \a option locally enabled
190         bool peerOption(option_type option); ///< \c true, if \a option enabled in peer
191
192     protected:
193         explicit BaseTelnetProtocol(Handle handle); ///< Construct telnet protocol handler
194         BaseTelnetProtocol();           ///< Provided for TelnetHandler mixins only
195
196         virtual ~BaseTelnetProtocol();
197
198         template <class Handler>
199         void registerHandler(Handler * h, bool request=true);
200                                         ///< Register a TelnetHandler
201
202         void incrementRequestCounter(); ///< Increment request counter
203                                         /**< This member may be called by a telnet handler to wait
204                                              for additional negotiations. It must be paired with a
205                                              corresponding decrementRequestCounter() call when that
206                                              negotiation is received. */
207         void decrementRequestCounter(); ///< Decrement request counter
208                                         /**< \see inrementRequestCounter() */
209         bool requestsPending();         ///< \c true, if there are pending requests
210
211 #ifndef DOXYGEN
212     private:
213 #endif
214         virtual void v_setupComplete() = 0; ///< Called, when no further requests are pending
215                                         /**< This callback will be called, when no further
216                                              negotiations are to be expected. The default
217                                              negotiation timeout is 500ms. If no reply is received
218                                              in this time, the request is abandoned and
219                                              v_setupComplete() is called.
220
221                                              mixin TelnetHandler's may additionally increment the
222                                              request counter to wait for specific
223                                              subnegotiations. v_setupComplete() will be called, when
224                                              all these negotiations are complete or have timed
225                                              out. */
226         virtual void v_charReceived(char c) = 0; ///< Called whenever a data character is received
227         virtual void v_eof() = 0;       ///< Called on input EOF
228
229         virtual void v_handleNOP();     ///< Called, when the peer sends a NOP
230         virtual void v_handleBRK();     ///< Called, when the peer sends a BReaK
231         virtual void v_handleIP();      ///< Called, when the peer sends an InterruptProcess
232         virtual void v_handleAO();      ///< Called, when the peer sends an AbortOutput
233         virtual void v_handleAYT();     ///< Called, when the peer sends an AreYouThere
234         virtual void v_handleEC();      ///< Called, when the peer sends an EraseCharacter
235         virtual void v_handleEL();      ///< Called, when the peer sends an EraseLine
236         virtual void v_handleGA();      ///< Called, when the peer sends a GoAhead
237
238     private:
239         void handleChar(char c);
240         void handleNormalChar(char c);
241         void handleCommand(char c);
242         void handleOption(char c);
243         void handleCR(char c);
244         void handleSBOption(char c);
245         void handleSBData(char c);
246         void handleSBIAC(char c);
247         void emit(char c);
248         void processCommand();
249         void transmit(char c);
250
251         void sendWILL(char option);
252         void sendWONT(char option);
253         void sendDO(char option);
254         void sendDONT(char option);
255
256         void readHandler(int state);
257         void writeHandler(int state);
258         void timeout();
259
260         enum Command {
261             CMD_NONE = 0,
262             CMD_SE = 240,
263             CMD_NOP = 241,
264             CMD_DM = 242,
265             CMD_BRK = 243,
266             CMD_IP = 244,
267             CMD_AO = 245,
268             CMD_AYT = 246,
269             CMD_EC = 247,
270             CMD_EL = 248,
271             CMD_GA = 249,
272             CMD_SB = 250,
273             CMD_WILL = 251,
274             CMD_WONT = 252,
275             CMD_DO = 253,
276             CMD_DONT = 254,
277             CMD_IAC = 255,
278         };
279
280         struct OptInfo
281         {
282             enum WantState { WANTED, ACCEPTED, DISABLED };
283             enum OptionState { NONE, REQUEST_SENT, ACKNOWLEDGED };
284
285             OptInfo();
286             OptInfo(bool local, option_type option);
287
288             //-////////////////////////////////////////////////////////////
289
290             bool const local;
291             option_type const option;
292
293             WantState wantState;
294             OptionState optionState;
295             bool enabled;
296
297         };
298
299         OptInfo & getOption(bool local, option_type option);
300         void request(OptInfo & info, bool enabled);
301         void response(OptInfo & info, bool enabled);
302
303         typedef std::map<std::pair<bool, option_type>, OptInfo> OptionsMap;
304         OptionsMap options_;
305
306         typedef std::map<option_type, TelnetHandler*> OptionHandlerMap;
307         OptionHandlerMap handlers_;
308
309         Handle handle_;
310
311         typedef std::vector<char> SendQueue;
312         SendQueue sendQueue_;
313
314         enum CharState { NORMAL, IAC_SEEN, EXPECT_OPTION, CR_SEEN,
315                          SB_OPTION, SB_DATA, SB_IAC_SEEN };
316         CharState charState_;
317
318         Command command_;
319         option_type option_;
320         std::string data_;
321
322         scheduler::FdEvent inputEvent_;
323         scheduler::FdEvent outputEvent_;
324
325         unsigned pendingRequests_;
326
327         ClockService::clock_type requestTimeout_;
328         scheduler::TimerEvent timeout_;
329
330         friend class TelnetHandler;
331     };
332
333     /** \brief Telnet handler base class
334
335         \see BaseTelnetProtocol
336      */
337     struct BaseTelnetProtocol::TelnetHandler
338         : public virtual BaseTelnetProtocol
339     {
340         virtual ~TelnetHandler();
341         virtual void v_init() = 0;      ///< Called, after option has been enabled
342         virtual void v_handleOptionParameters(std::string const & data) = 0;
343                                         ///< Called, whenever a subnegotiation is received
344     };
345
346     /** \brief Telnet option codes
347
348         See http://www.iana.org/assignments/telnet-options for a list of options
349
350         \ingroup telnet_group
351      */
352     namespace telnetopt { BaseTelnetProtocol::option_type const ECHO = 1u; }
353     namespace telnetopt { BaseTelnetProtocol::option_type const TRANSMIT_BINARY = 0u; }
354     namespace telnetopt { BaseTelnetProtocol::option_type const SUPPRESS_GO_AHEAD = 3u; }
355     namespace telnetopt { BaseTelnetProtocol::option_type const TERMINAL_TYPE = 24u; }
356     namespace telnetopt { BaseTelnetProtocol::option_type const NAWS = 31u; }
357     namespace telnetopt { BaseTelnetProtocol::option_type const LINEMODE = 34u; }
358
359 /** \brief Telnet handlers
360
361     \ingroup telnet_group */
362 namespace telnethandler {
363
364     /** \brief Implement TERMINAL_TYPE option
365
366         This telnet handler implements the TERMINAL_TYPE option. The handler automatically requests
367         the first terminal type during initialization. Further terminal types may then be requested
368         by calling nextTerminalType().
369
370         The last received terminal type will be returned by the terminalType() member.
371
372         This implementation only provides server support (querying the terminal type).
373
374         \see BaseTelnetProtocol for how to integrate this handler \n
375             <a href="http://tools.ietf.org/html/rfc1091">RFC 1091</a> Telnet Terminal-Type Option
376      */
377     class TerminalType
378         : public BaseTelnetProtocol::TelnetHandler
379     {
380     public:
381         static option_type const OPTION_CODE = telnetopt::TERMINAL_TYPE;
382
383         void nextTerminalType();        ///< Request another terminal type
384         std::string const & terminalType() const; ///< Return current terminal type
385
386     protected:
387         TerminalType();
388
389     private:
390         virtual void v_init();
391         virtual void v_handleOptionParameters(std::string const & data);
392
393         std::string type_;
394     };
395
396     /** \brief Implement NAWS (Negotiation About Window Size) option
397
398         This telnet handler implements the NAWS option. The client terminals window size will be
399         requested during initialization. The current window size may always be accessed using the
400         width() and height() members.
401
402         Whenever the window size is changed, the v_windowSizeChanged() function is called. This
403         function must be implemented in a derived class.
404
405         \see BaseTelnetProtocol for how to integrate this handler \n
406             <a href="http://tools.ietf.org/html/rfc1073">RFC 1073</a> Telnet Window Size Option
407      */
408     class NAWS
409         : public BaseTelnetProtocol::TelnetHandler
410     {
411     public:
412         static option_type const OPTION_CODE = telnetopt::NAWS;
413
414         unsigned width() const;         ///< Get current client window width
415         unsigned height() const;        ///< Get current client window height
416
417     protected:
418         NAWS();
419
420 #   ifndef DOXYGEN
421     private:
422 #   endif
423         virtual void v_windowSizeChanged() = 0; ///< Called, whenever window size changes
424                                         /**< This member is called for every window size change \e
425                                              after initialization. */
426
427     private:
428         virtual void v_init();
429         virtual void v_handleOptionParameters(std::string const & data);
430
431         unsigned width_;
432         unsigned height_;
433     };
434
435 }
436
437 }}
438
439 //-/////////////////////////////////////////////////////////////////////////////////////////////////
440 #include "Telnet.cci"
441
442 //#include "Telnet.ct"
443 #include "Telnet.cti"
444 #endif
445
446 \f
447 // Local Variables:
448 // mode: c++
449 // fill-column: 100
450 // comment-column: 40
451 // c-file-style: "senf"
452 // indent-tabs-mode: nil
453 // ispell-local-dictionary: "american"
454 // compile-command: "scons -u test"
455 // End: