A telnet library (including ipaccess-telnet)
Go to file
Sean Middleditch d58f49f03a add printf data sender 2009-03-16 12:49:35 -04:00
.gitignore build/dist updates 2009-03-16 01:42:33 -04:00
Makefile build/dist updates 2009-03-16 01:42:33 -04:00
README minor cleanup, added libtelnet_send_telopt() 2009-03-16 01:25:52 -04:00
libtelnet.c add printf data sender 2009-03-16 12:49:35 -04:00
libtelnet.h add printf data sender 2009-03-16 12:49:35 -04:00
telnet-client.c Merge branch 'master' into rfc1143 2009-03-15 23:46:31 -04:00
telnet-proxy.c dont bail on connetion reset return on send() 2009-03-16 11:27:38 -04:00

README

=====================================================================
  libtelnet - TELNET protocol handling library
=====================================================================

 http://github.com/elanthis/libtelnet

 Sean Middleditch
 sean@sourcemud.org

---------------------------------------------------------------------
The author or authors of this code dedicate any and all copyright
interest in this code to the public domain. We make this dedication
for the benefit of the public at large and to the detriment of our
heirs and successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
code under copyright law. 
---------------------------------------------------------------------

*** TODO ***

 - automatic MCCP2 handling (controllable by host app)
 ? ZMP parsing
 ? MSSP parsing
 ? ENVIRON/NEW-ENVIRON parsing
 ? telnet-status testing tool

I. INTRODUCTION
=====================================================================

libtelnet provides safe and correct handling of the core TELNET
protocol.  In addition to the base TELNET protocol, libtelnet also
implements the Q method of TELNET option negotiation.  libtelnet
can be used for writing servers, clients, or proxies.

For more information on the TELNET protocol, see:

 http://www.faqs.org/rfcs/rfc854.html
 http://www.faqs.org/rfcs/rfc1143.html

II. LIBTELNET API
=====================================================================

The libtelnet API contains several distinct parts.  The first part is
the basic initialization and deinitialization routines.  The second
part is a single function for pushing received data into the
libtelnet processor.  The third part is the libtelnet_send_*()
functions, which generate TELNET commands and ensure data is properly
formatted before sending over the wire.  The final part is the event
handler interface.

IIa. Initialization

 struct libtelnet_t;
   This structure represents the state of the TELNET protocol for a
   single connection.  Each connection utilizing TELNET must have
   its own libtelnet_t structure, which is passed to all libtelnet
   API calls.

 void libtelnet_init(libtelnet_t *telnet, libtelnet_event_handler_t handler,
     unsigned char flags, void *user_data);
   The libtelnet_init() function is responsible for initializing
   the data in a libtelnet_t structure.  It must be called
   immediately after establishing a connection and before any other
   libtelnet API calls are made.

   The handler parameter must be a function matching the
   libtelnet_event_handler_t definition.  More information about
   events can be found in section IId.

   The user_data parameter is passed to the event handler whenver it
   is invoked.  This will usually be a structure container
   information about the connection, including a socket descriptor
   for implementing LIBTELNET_EV_SEND event handling.

   The flags parameter can be any of the following flag constants
   bit-or'd together, or 0 to leave all options disabled.

    LIBTELNET_FLAG_PROXY   - operate in proxy mode
 
 boid libtelnet_free(libtelnet_t *telnet);
   Releases any internal memory allocated by libtelnet.  This must
   be called whenever a connection is closed, or you will incur
   memory leaks.

IIb. Receiving Data

 void libtelnet_push(libtelnet_t *telnet,
     unsigned char *buffer, unsigned int size, void *user_data);
   When your application receives data over the socket from the
   remote end, it must pass the received bytes into this function.

   As the TELNET stream is parsed, events will be generated and
   passed to the event handler given to libtelnet_init().  Of
   particular interest for data receiving is the LIBTELNET_EV_DATA
   event, which is triggered for any regular data such as user
   input or server process output.

IIc. Sending Data

 All of the libtelnet_send_*() functions will invoke the
 LIBTELNET_EV_SEND event.

 Note: it is very important that ALL data sent to the remote end of
 the connection be passed through libtelnet.  All user input or
 process output that you wish to send over the wire should be given
 to libtelnet_send_data().  Do NOT send or buffer unprocessed output
 data directly!

 void libtelnet_send_command(libtelnet_t *telnet, unsigned char cmd);
   Sends a single "simple" TELNET command, such as the GO-AHEAD
   commands (255 249).

 void libtelnet_send_command(libtelnet_t *telnet, unsigned char cmd,
     unsigned char telopt);
   Sends a TELNET command with an option code following.  This is
   only useful for the WILL, WONT, DO, DONT, and SB commands.

 void libtelnet_send_negotiate(libtelnet_t *telnet,
     unsigned char cmd, unsigned char opt);
   Sends a TELNET negotiation command.  The cmd parameter must be
   one of LIBTELNET_WILL, LIBTELNET_DONT, LIBTELNET_DO, or
   LIBTELNET_DONT.  The opt parameter is the option to
   negotiate.

 void libtelnet_send_data(libtelnet_t *telnet, unsigned char *buffer,
     unsigned int size);
   Sends raw data, which would be either the process output from
   a server or the user input from a client.

 void libtelnet_send_subnegotiation(libtelnet_t *telnet,
     unsigned char telopt, unsigned char *buffer, unsigned int size);
   Sends a TELNET sub-negotiation command.  The telopt parameter
   is the sub-negotiation option.

   Note that the above function is just a shorthand for:
    libtelnet_send_telopt(telnet, LIBTELNET_SB, telopt);
    libtelnet_send_data(telnet, buffer, size);
    libtelnet_send_command(telnet, LIBTELNET_SE);

   For some subnegotiations that involve a lot of complex formatted
   data to be sent, it may be easier to manually send the SB telopt
   header and SE footer around mulitple calls to send_data.

   NOTE: libtelnet_send_subrequest() does have special behavior in
   PROXY mode, as in that mode this function will automatically
   detect the COMPRESS2 marker and enable zlib compression.

IId. Event Handling

 libtelnet relies on an event-handling mechanism for processing
 the parsed TELNET protocol stream as well as for buffering and
 sending output data.

 When you initialize a libtelnet_t structure with libtelnet_init()
 you had to pass in an event handler function.  This function must
 meet the following prototype:

  void (libtelnet_t *telnet, libtelnet_event_t *event,
      void *user_data);
 
 The event structure is detailed below.  The user_data value is the
 pointer passed to libtelnet_init().

  struct libtelnet_event_t {
    unsigned char *buffer;
    unsigned int size;
    libtelnet_event_type_t type;
    unsigned char command;
    unsigned char telopt;
    unsigned char accept;
  };
 
 The enumeration values of libtelnet_event_type_t are described in
 detail below.  Whenever the the event handler is invoked, the
 application must look at the event->type value and do any
 necessary processing.

 The only event that MUST be implemented is LIBTELNET_EV_SEND.
 Most applications will also always want to implement the event
 LIBTELNET_EV_DATA.

 Here is an example event handler implementation which includes
 handlers for several important events.

  void my_event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
      void *user_data) {
    struct user_info *user = (struct user_info *)user_data;

    switch (ev->type) {
    case LIBTELNET_EV_DATA:
      process_user_input(user, event->buffer, event->size);
      break;
    case LIBTELNET_EV_SEND:
      write_to_descriptor(user, event->buffer, event->size);
      break;
    case LIBTELNET_EV_ERROR:
      fatal_error("TELNET error: %s", event->buffer);
      break;
    }
  }

 LIBTELNET_EV_DATA:
   The DATA event is triggered whenever regular data (not part of
   any special TELNET command) is received.  For a client, this
   will be process output from the server.  For a server, this will
   be input typed by the user.

   The event->buffer value will contain the bytes received and the
   event->size value will contain the number of bytes received.
   Note that event->buffer is not NUL terminated!

   NOTE: there is no guarantee that user input or server output
   will be received in whole lines.  If you wish to process data
   a line at a time, you are responsible for buffering the data and
   checking for line terminators yourself!
 
 LIBTELNET_EV_SEND:
   This event is sent whenever libtelnet has generated data that
   must be sent over the wire to the remove end.  Generally that
   means calling send() or adding the data to your application's
   output buffer.

   The event->buffer value will contain the bytes to send and the
   event->size value will contain the number of bytes to send.
   Note that event->buffer is not NUL terminated, and may include
   NUL characters in its data, so always use event->size!

   NOTE: Your SEND event handler must send or buffer the data in
   its raw form as provided by libtelnet.  If you wish to perform
   any kind of preprocessing on data you want to send to the other
 
 LIBTELNET_EV_IAC:
   The IAC event is triggered whenever a simple IAC command is
   received, such as the IAC EOR (end of record, also called
   go ahead or GA) command.

   The command received is in the event->command value.

   The necessary processing depends on the specific commands; see
   the TELNET RFC for more information.
 
 LIBTELNET_EV_WILL:
 LIBTELNET_EV_DO:
   The WILL and DO events are sent when a TELNET negotiation
   command of the same name is received.

   WILL events are sent by the remote end when they wish to be
   allowed to turn an option on on their end, or in confirmation
   after you have sent a DO command to them.

   DO events are sent by the remote end when they wish for you
   to turn on an option on your end, or in confirmation after you
   have sent a WILL command to them.

   In either case, the TELNET option under negotiation will be in
   event->telopt field.

   If you support the option and wish for it to be enabled you
   must set the event->accept field to 1, unless this event is
   a confirmation for a previous WILL/DO command you sent to the
   remote end.  If you do not set event->field to 1 then
   libtelnet will send a rejection command back to the other end.

   libtelnet manages some of the pecularities of negotiation for
   you.  For information on libtelnet's negotiation method, see:

    http://www.faqs.org/rfcs/rfc1143.html

   Examples:

    You want remote end to use TTYPE, so you send DO TTYPE.
    Remote accepts and sends WILL TTYPE.

    Remote end wants you to use SGA, so they send DO_SGA.
    You do not support SGA and set event->accept = 0.

    Remote end wants to use ZMP, so they send WILL ZMP.
    You support ZMP, so you set event->accept = 1 and enable
    local ZMP support.

    You want to use MCCP2, so you send WILL COMPRESS2.
    Remote end accepts and sends DO COMPRESS2.

   Note that in PROXY mode libtelnet will do no processing of its
   own for you.

 LIBTELNET_EV_WONT:
 LIBTELNET_EV_DONT:
   The WONT and DONT events are sent when the remote end of the
   connection wishes to disable an option, when they are
   refusing to a support an option that you have asked for, or
   in confirmation of an option you have asked to be disabled.

   Most commonly WONT and DONT events are sent as rejections of
   features you requested by sending DO or WILL events.  Receiving
   these events means the TELNET option is not or will not be
   supported by the remote end, so give up.

   Sometimes WONT or DONT will be sent for TELNET options that are
   already enabled, but the remote end wishes to stop using.  You
   cannot decline.  These events are demands that must be complied
   with.  libtelnet will always send the appropriate response back
   without consulting your application.  These events are sent to
   allow your application to disable its own use of the features.

   In either case, the TELNET option under negotiation will be in
   event->telopt field.

   Note that in PROXY mode libtelnet will do no processing of its
   own for you.

 LIBTELNET_EV_SUBNEGOTIATION:
   Triggered whenever a TELNET sub-negotiation has been received.
   Sub-negotiations include the NAWS option for communicating
   terminal size to a server, the NEW-ENVIRON and TTYPE options
   for negotiating terminal features, and MUD-centric protocols
   such as ZMP, MSSP, and MCCP2.

   The event->telopt value is the option under sub-negotiation.
   The remaining data (if any) is passed in event->buffer and
   event->size.  Note that most subnegotiation commands can
   include embedded NUL bytes in the subnegotiation data, and
   the data event->buffer is not NUL terminated, so always use
   the event->size value!

   The meaning and necessary processing for subnegotiations are
   defined in various TELNET RFCs and other informal
   specifications.  A subnegotiation should never be sent unless
   the specific option has been enabled through the use of the
   telnet negotiation feature.

 LIBTELNET_EV_COMPRESS
   The COMPRESS event notifies the app that COMPRESS2/MCCP2
   compression has begun or ended.  Only servers can send compressed
   data, and hence only clients will receive compressed data.

   The event->command value will be 1 if compression has started and
   will be 0 if compression has ended.
 
 LIBTELNET_EV_WARNING
   The WARNING event is sent whenever something has gone wrong
   inside of libtelnet (possibly due to malformed data sent by the
   other end) but which recovery is (likely) possible.  It may be
   safe to continue using the connection, but some data may have
   been lost or incorrectly interpreted.

   The event->buffer value will contain a NUL terminated string
   explaining the error, and the event->size value containers the
   length of the string.

 LIBTELNET_EV_ERROR
   Similar to the WARNING event, the ERROR event is sent whenever
   something has gone wrong.  ERROR events are non-recoverable,
   however, and the application should immediately close the
   connection.  Whatever has happened is likely going only to
   result in garbage from libtelnet.  This is most likely to
   happen when a COMPRESS2 stream fails, but other problems can
   occur.

   The event->buffer value will contain a NUL terminated string
   explaining the error, and the event->size value containers the
   length of the string.

III. INTEGRATING LIBTELNET WITH COMMON MUDS
=====================================================================

FIXME: fill in some notes about how to splice in libtelnet with
common Diku/Merc/Circle/etc. MUD codebases.

IV. SAFETY AND CORRECTNESS CONSIDERATIONS
=====================================================================

Your existing application may make heavy use of its own output
buffering and transmission commands, including hand-made routines
for sending TELNET commands and sub-negotiation requests.  There are
at times subtle issues that need to be handled when communication
over the TELNET protocol, not least of which is the need to escape
any byte value 0xFF with a special TELNET command.

For these reasons, it is very important that applications making use
of libtelnet always make use of the libtelnet_send_*() family of
functions for all data being sent over the TELNET connection.

In particular, if you are writing a client, all user input must be
passed through to libtelnet_send_data().  This also includes any
input generated automatically by scripts, triggers, or macros.

For a server, any and all output -- including ANSI/VT100 escape
codes, regular text, newlines, and so on -- must be passed through
to libtelnet_send_data().

Any TELNET commands that are to be sent must be given to one of the
following: libtelnet_send_command, libtelnet_send_negotiate, or
libtelnet_send_subnegotiation().

If you are attempting to enable COMPRESS2/MCCP2, you must use the
libtelnet_begin_compress2() function.

V. MCCP2 COMPRESSION
=====================================================================

The MCCP2 (COMPRESS2) TELNET extension allows for the compression of
all traffic sent from server to client.  For more information:

 http://www.mudbytes.net/index.php?a=articles&s=mccp

In order for libtelnet to support MCCP2, zlib must be installed and
enabled when compiling libtelnet.  Use -DHAVE_ZLIB to enable zlib
when compiling libtelnet.c and pass -lz to the linker to link in the
zlib shared library.

libtelnet transparently supports MCCP2.  For a server to support
MCCP2, the application must begin negotiation of the COMPRESS2
option using libtelnet_send_negotiate(), for example:

 libtelnet_send_negotiate(&telnet, LIBTELNET_WILL,
     LIBTELNET_OPTION_COMPRESS2, user_data);

If a favorable DO COMPRESS2 is sent back from the client then the
server application can begin compression at any time by calling
libtelnet_begin_compress2().

If a connection is in PROXY mode and COMPRESS2 support is enabled
then libtelnet will automatically detect the start of a COMPRESS2
stream, in either the sending or receiving direction.

VI. TELNET PROXY UTILITY
=====================================================================

The telnet-proxy utility is a small application that serves both as
a testbed for libtelnet and as a powerful debugging tool for TELNET
servers and clients.

To use telnet-proxy, you must first compile it using:

 $ make

If you do not have zlib installed and wish to disable MCCP2 support
then you must first edit the Makefile and remove the -DHAVE_ZLIB and
the -lz from the compile flags.

To run telnet-proxy, you simply give it the server's host name or
IP address, the server's port number, and the port number that
telnet-proxy should listen on.  For example, to connect to the server
on mud.example.com port 7800 and to listen on port 5000, run:

 $ ./telnet-proxy mud.example.com 7800 5000

You can then connect to the host telnet-proxy is running on (e.g.
127.0.0.1) on port 500 and you will automatically be proxied into
mud.example.com.

telnet-proxy will display status information about the data
passing through both ends of the tunnel.  telnet-proxy can only
support a single tunnel at a time.  It will continue running until
an error occurs or a terminating signal is sent to the proxy
process.