| ===================================================================== |
| 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 *** |
| |
| - RFC 1143 option negotiation algorithm |
| - automatic MCCP2 handling (controllable by host app) |
| - efficient one-byte sub-requests |
| ? MCCP1 |
| ? ZMP parsing |
| ? MSSP parsing |
| ? ENVIRON/NEW-ENVIRON parsing |
| ? telnet-status testing tool |
| ? few options to make telnet-proxy even more useful |
| |
| 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. |
| |
| For more information on the TELNET protocol, see: |
| |
| http://www.faqs.org/rfcs/rfc854.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 |
| callback structure libtelnet_cb_t. |
| |
| 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. |
| |
| struct libtelnet_cb_t; |
| An instance of this structure must be initialized and have all |
| mandatory and desired optional callbacks set. See section IId |
| for more information. |
| |
| void libtelnet_init(struct libtelnet_t *telnet, |
| struct libtelnet_cb_t *cb, enum libtelnet_mode_t mode); |
| 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 cb parameter must be a pointer to a fully initialized |
| instance of libtelnet_cb_t. A single instance of the structure |
| can be shared between any number of libtelnet_t instances. |
| |
| The mode parameter must be one of LIBTELNET_MODE_SERVER or |
| LIBTELNET_MODE_CLIENT. 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. |
| |
| 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. |
| Callback functions will be invoked as the buffer is processed, |
| and the user_data parameter will be passed to each callback. |
| |
| IIc. Sending Data |
| |
| Note that all of the libtelnet_send_*() functions will invoke |
| the send callback function attached to the libtelnet_t instance. |
| The user_data parameter to each of these functions is passed |
| through to the callback. |
| |
| void libtelnet_send_command(struct libtelnet_t *telnet, |
| unsigned char cmd, void *user_data); |
| 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, void *user_data); |
| 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, void *user_data); |
| 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, |
| void *user_data); |
| Sends a TELNET sub-negotiation command. The opt parameter |
| is the sub-negotiation option. |
| |
| IId. Callbacks |
| |
| The libtelnet_cb_t structure containers a number of callback |
| entry points. Of these, only the send and data callbacks are |
| absolutely required. All others are optional. The declarations |
| below show the signature of the callback functions. |
| |
| An example of initializing a libtelnet_cb_t structure: |
| |
| /* illustrative data callback */ |
| void my_data_cb(libtelnet_t *telnet, unsigned char *buffer, |
| unsigned int size, void *user_data) { |
| /* print number of bytes received and then show the |
| * whole buffer */ |
| printf("RECV(%d): %.*s\n", size, size, buffer); |
| } |
| |
| /* illustrative variable definitions */ |
| libtelnet_t conn; |
| libtelnet_cb_t callbacks; |
| |
| /* clear all callbacks and set just the ones we want */ |
| memset(&callbacks, 0, sizeof(callbacks)); |
| callbacks->send = my_send_cb; |
| callbacks->data = my_data_cb; |
| |
| /* initialize the connection with our callbacks */ |
| libtelnet_init(&conn, &callbacks, LIBTELNET_MODE_SERVER); |
| |
| Remember that a single libtelnet_cb_t structure can be shared |
| between any number of libtelnet_t instances. There is no reason |
| to make multiple copies of the data if all of your connections |
| use the same callback functions. |
| |
| void libtelnet_cb_t->data(struct libtelnet_t *telnet, |
| unsigned char *buffer, unsigned int size, void *user_data); |
| Regular data has been received by the remote end. For a server, |
| this would be input typed by the client; for a client, this is |
| process output generated by the server. |
| |
| Note that data is not line-buffered by libtelnet. A single |
| line of input may be broken into pieces and given to |
| consecutive calls to libtelnet_data_cb(). If you are doing |
| line-based processing of data, it is your responsibility to |
| buffer data and find the line breaks. |
| |
| void libtelnet_cb_t->send(struct libtelnet_t *telnet, |
| unsigned char *buffer, unsigned int size, void *user_data); |
| This is called whenever libtelnet has generated output to be |
| send to the remote end of the connection. In most cases this |
| will be a simple wrapper arround your applications network |
| output buffering/transmission code. |
| |
| You can pass socket information through the user_data |
| parameter to libtelnet calls so that it is available in this |
| callback. |
| |
| void libtelnet_cb_t->command(struct libtelnet_t *telnet, |
| unsigned char cmd, void *user_data); |
| Called whenever a "simpler" TELNET command has arrived, such |
| as GO-AHEAD commands (255 249). The necessary processing |
| depends on the specific commands; see the TELNET RFC for |
| more information. |
| |
| void libtelnet_cb_t->negotiate(struct libtelnet_t *telnet, |
| unsigned char cmd, unsigned char opt, void *user_data); |
| This function is called whenever a TELNET negotiation command |
| has been received. The cmd parameter will be one of |
| LIBTELNET_WILL, LIBTELNET_WONT, LIBTELNET_DO, or LIBTELNET_DONT. |
| The opt parameter is the option being negotiated. |
| |
| libtelnet does not currently manage negotiation for you. For |
| best practice in implementing TELNET negotiation, see: |
| |
| http://www.faqs.org/rfcs/rfc1143.html |
| |
| void libtelnet_cb_t->subnegotiation(struct libtelnet_t *telnet, |
| unsigned char opt, unsigned char *data, unsigned int size, |
| void *user_data); |
| Called 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 opt parameter is the option under sub-negotiation. The |
| remaining data (if any) is passed in the buffer. |
| |
| void libtelnet_cb_t->compress(struct libtelnet_t *telnet, |
| char enabled, void *user_data); |
| The callback is invoked whenever the COMPRESS2 (MCCP2) |
| feature is enabled or disabled. For servers, this is called |
| immediately after beginning compression after a client accepts |
| the COMPRESS2 option. For clients, this is called immediately |
| after a compress stream begin or ends. |
| |
| The enabled parameter is 1 if compression has begun and 0 if |
| compression has ended. |
| |
| III. INTEGRATING LIBTELNET |
| ===================================================================== |
| |
| FIXME: fill in notes about implementing the libtelnet_*_cb functions |
| |
| 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. |
| |
| 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 |
| |
| 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); |
| |
| libtelnet will automatically detect if the client responds favoribly |
| and will begin compressing data. For clients, no action must be |
| taken, as libtelnet will automatically handle the requests. |
| |
| NOTE: libtelnet will still invoke the callback functions for |
| negotiation and sub-negotiation commands relating to MCCP2. Do not |
| respond to these. |
| |
| 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. |
| |
| 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 IP address |
| (telnet-proxy does not support hostname resolution, nor IPv6), the |
| server's port number, and the port number that telnet-proxy should |
| listen on. For example, to connect to the server on 209.85.171.100 |
| port 7800 and to listen on port 5000, run: |
| |
| $ ./telnet-proxy 209.85.171.100 7800 5000 |
| |
| You can then connect to the host telnet-proxy is running on (e.g. |
| 127.0.0.1, or the machine's public IP) on port 500 and you will |
| automatically be proxied into 209.85.171.100. |
| |
| telnet-proxy will display status information about the data |
| passing through both ends of the tunnel. Once either end |
| disconnects, telnet-proxy will close. telnet-proxy can only |
| support a single tunnel at a time and must be restarted for each |
| connection. |