libtelnet/README

391 lines
15 KiB
Plaintext
Raw Normal View History

2009-03-13 03:33:27 +00:00
=====================================================================
libtelnet - TELNET protocol handling library
=====================================================================
2009-03-14 17:39:07 +00:00
http://github.com/elanthis/libtelnet
Sean Middleditch
sean@sourcemud.org
2009-03-13 03:33:27 +00:00
---------------------------------------------------------------------
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.
---------------------------------------------------------------------
2009-03-14 09:24:56 +00:00
*** TODO ***
- RFC 1143 option negotiation algorithm
- automatic MCCP2 handling (controllable by host app)
? ZMP parsing
? MSSP parsing
? ENVIRON/NEW-ENVIRON parsing
? telnet-status testing tool
2009-03-13 03:33:27 +00:00
I. INTRODUCTION
=====================================================================
libtelnet provides safe and correct handling of the core TELNET
protocol. It does not include any "smarts," and all use of the
protocol (such as deciding which options to support, enabling
and disabling options, or processing subrequests) must be implemented
by the application author.
2009-03-14 17:39:07 +00:00
For more information on the TELNET protocol, see:
http://www.faqs.org/rfcs/rfc854.html
2009-03-13 03:33:27 +00:00
II. LIBTELNET API
=====================================================================
2009-03-14 17:39:07 +00:00
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.
2009-03-14 17:39:07 +00:00
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(struct libtelnet_t *telnet,
libtelnet_event_handler_t handler, enum libtelnet_mode_t mode,
void *user_data);
2009-03-14 17:39:07 +00:00
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.
2009-03-15 15:54:07 +00:00
The mode parameter must be one of LIBTELNET_MODE_SERVER,
LIBTELNET_MODE_CLIENT, or LIBTELNET_MODE_PROXY. These slightly
alter the behavior of libtelnet in certain instances. If you are
implementing a TELNET server, use the SERVER mode. If you are
implementing a client, use the CLIENT mode. The PROXY mode
enables special behavior for telnet-proxy (or similar
applications).
2009-03-14 17:39:07 +00:00
boid libtelnet_free(struct 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(struct 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.
2009-03-14 17:39:07 +00:00
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!
2009-03-14 17:39:07 +00:00
void libtelnet_send_command(struct libtelnet_t *telnet,
unsigned char cmd);
2009-03-14 17:39:07 +00:00
Sends a single "simple" TELNET command, such as the GO-AHEAD
commands (255 249).
void libtelnet_send_negotiate(struct libtelnet_t *telnet,
unsigned char cmd, unsigned char opt);
2009-03-14 17:39:07 +00:00
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(struct libtelnet_t *telnet,
unsigned char *buffer, unsigned int size);
2009-03-14 17:39:07 +00:00
Sends raw data, which would be either the process output from
a server or the user input from a client.
void libtelnet_send_subnegotiation(struct libtelnet_t *telnet,
unsigned char opt, unsigned char *buffer, unsigned int size);
2009-03-14 17:39:07 +00:00
Sends a TELNET sub-negotiation command. The opt parameter
is the sub-negotiation option.
IId. Event Handling
2009-03-14 17:39:07 +00:00
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 {
enum libtelnet_event_type_t type;
unsigned char command;
unsigned char telopt;
unsigned char *buffer;
unsigned int size;
};
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(struct 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;
}
}
2009-03-14 17:39:07 +00:00
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.
2009-03-14 17:39:07 +00:00
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!
2009-03-14 17:39:07 +00:00
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.
2009-03-14 17:39:07 +00:00
The command received is in the event->command value.
2009-03-14 17:39:07 +00:00
The necessary processing depends on the specific commands; see
the TELNET RFC for more information.
LIBTELNET_EV_NEGOTIATE:
The NEGOTIATE event is sent when a TELNET neogitiation command
is received.
2009-03-14 17:39:07 +00:00
The event->command value will be one of LIBTELNET_WILL,
LIBTELNET_WONT, LIBTELNET_DO, or LIBTELNET_DONT. The
event->telopt value will contain the option value being
negotiated.
2009-03-14 17:39:07 +00:00
libtelnet does not currently manage negotiation for you. For
best practice in implementing TELNET negotiation, see:
http://www.faqs.org/rfcs/rfc1143.html
LIBTELNET_EV_SUBNEGOTIATION:
Triggered whenever a TELNET sub-negotiation has been received.
2009-03-14 17:39:07 +00:00
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
2009-03-13 03:33:27 +00:00
=====================================================================
FIXME: fill in some notes about how to splice in libtelnet with
common Diku/Merc/Circle/etc. MUD codebases.
2009-03-13 03:33:27 +00:00
2009-03-14 17:39:07 +00:00
IV. SAFETY AND CORRECTNESS CONSIDERATIONS
2009-03-13 03:33:27 +00:00
=====================================================================
2009-03-14 17:39:07 +00:00
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.
2009-03-13 03:33:27 +00:00
2009-03-14 17:39:07 +00:00
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.
2009-03-14 17:39:07 +00:00
V. MCCP2 COMPRESSION
2009-03-13 03:33:27 +00:00
=====================================================================
2009-03-14 17:39:07 +00:00
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.
2009-03-14 17:39:07 +00:00
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 (processed
in a LIBTELNET_EV_NEGOTIATE event, with event->command equal to
LIBTELNET_DO and event->telopt equal to LIBTELNET_TELOPT_COMPRESS2),
then the server application can begin compression at any time by
calling libtelnet_begin_compress2().
2009-03-14 17:39:07 +00:00
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.
2009-03-14 17:39:07 +00:00
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.
2009-03-15 05:06:17 +00:00
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:
2009-03-14 17:39:07 +00:00
2009-03-15 05:06:17 +00:00
$ ./telnet-proxy mud.example.com 7800 5000
2009-03-14 17:39:07 +00:00
You can then connect to the host telnet-proxy is running on (e.g.
2009-03-15 05:06:17 +00:00
127.0.0.1) on port 500 and you will automatically be proxied into
mud.example.com.
2009-03-14 17:39:07 +00:00
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.