mirror of https://gerrit.osmocom.org/libtelnet
scrap the multiple callbacks thing, just use a single event handler callback
This commit is contained in:
parent
f66a7ee768
commit
637df7f064
282
README
282
README
|
@ -49,8 +49,8 @@ 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.
|
||||
formatted before sending over the wire. The final part is the event
|
||||
handler interface.
|
||||
|
||||
IIa. Initialization
|
||||
|
||||
|
@ -60,21 +60,16 @@ IIa. Initialization
|
|||
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);
|
||||
libtelnet_event_handler_t handler, 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 handler parameter must be a function matching the
|
||||
libtelnet_event_handler_t definition. More information about
|
||||
events can be found in section IId.
|
||||
|
||||
The mode parameter must be one of LIBTELNET_MODE_SERVER,
|
||||
LIBTELNET_MODE_CLIENT, or LIBTELNET_MODE_PROXY. These slightly
|
||||
|
@ -95,15 +90,24 @@ IIb. Receiving Data
|
|||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
All of the libtelnet_send_*() functions will invoke the
|
||||
LIBTELNET_EV_SEND event. The user_data parameter to each of these
|
||||
functions is passed through to the callback.
|
||||
|
||||
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(struct libtelnet_t *telnet,
|
||||
unsigned char cmd, void *user_data);
|
||||
|
@ -128,109 +132,164 @@ IIc. Sending Data
|
|||
Sends a TELNET sub-negotiation command. The opt parameter
|
||||
is the sub-negotiation option.
|
||||
|
||||
IId. Callbacks
|
||||
IId. Event Handling
|
||||
|
||||
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.
|
||||
libtelnet relies on an event-handling mechanism for processing
|
||||
the parsed TELNET protocol stream as well as for buffering and
|
||||
sending output data.
|
||||
|
||||
An example of initializing a libtelnet_cb_t structure:
|
||||
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:
|
||||
|
||||
/* 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);
|
||||
void (libtelnet_t *telnet, libtelnet_event_t *event,
|
||||
void *user_data);
|
||||
|
||||
The libtelnet_event_t structure has the following definition:
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* illustrative variable definitions */
|
||||
libtelnet_t conn;
|
||||
libtelnet_cb_t callbacks;
|
||||
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.
|
||||
|
||||
/* 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;
|
||||
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!
|
||||
|
||||
/* initialize the connection with our callbacks */
|
||||
libtelnet_init(&conn, &callbacks, LIBTELNET_MODE_SERVER);
|
||||
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.
|
||||
|
||||
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.
|
||||
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!
|
||||
|
||||
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: 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.
|
||||
|
||||
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.
|
||||
The command received is in the event->command value.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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 opt parameter is the option under sub-negotiation. The
|
||||
remaining data (if any) is passed in the buffer.
|
||||
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!
|
||||
|
||||
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 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.
|
||||
|
||||
The enabled parameter is 1 if compression has begun and 0 if
|
||||
compression has ended.
|
||||
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.
|
||||
|
||||
III. INTEGRATING LIBTELNET
|
||||
The event->command value will be 1 if compression has started and
|
||||
will be 0 if compression has ended.
|
||||
|
||||
LIBTELNET_EV_ERROR
|
||||
This event is called whenever an error occurs while trying to
|
||||
process the TELNET protocol. This includes both invalid protocol
|
||||
sequences (which are rare) and out of memory conditions.
|
||||
|
||||
With few exceptions, an error is non-recoverable, and the only
|
||||
solid course of action is to close the connection. This is
|
||||
especially true for any errors involving the COMPRESS2 option.
|
||||
|
||||
The event->buffer value will contain a NUL terminated string
|
||||
explaining the error, and the event->size value containers the
|
||||
length of the string.
|
||||
|
||||
FIXME: we should pass the error code in one of the fields, and
|
||||
better document which errors are definitely non-recoverable and
|
||||
which are maybe-recoverable (mostly those are just IAC-in-SB
|
||||
errors... every other error is related to MCCP2 and usually
|
||||
results in being unable to further read the stream).
|
||||
|
||||
III. INTEGRATING LIBTELNET WITH COMMON MUDS
|
||||
=====================================================================
|
||||
|
||||
FIXME: fill in notes about implementing the libtelnet_*_cb functions
|
||||
FIXME: fill in some notes about how to splice in libtelnet with
|
||||
common Diku/Merc/Circle/etc. MUD codebases.
|
||||
|
||||
IV. SAFETY AND CORRECTNESS CONSIDERATIONS
|
||||
=====================================================================
|
||||
|
@ -246,6 +305,21 @@ 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
|
||||
=====================================================================
|
||||
|
||||
|
@ -254,6 +328,11 @@ 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:
|
||||
|
@ -261,18 +340,15 @@ 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.
|
||||
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().
|
||||
|
||||
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.
|
||||
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
|
||||
=====================================================================
|
||||
|
|
71
libtelnet.c
71
libtelnet.c
|
@ -22,10 +22,10 @@
|
|||
|
||||
#include "libtelnet.h"
|
||||
|
||||
/* error handler helpers */
|
||||
#ifdef ERROR
|
||||
# undef ERROR
|
||||
#endif
|
||||
|
||||
#define ERROR(telnet, code, user_data, msg) \
|
||||
_error(telnet, __FILE__, __LINE__, code, user_data, "%s", msg)
|
||||
#define ERROR_NOMEM(telnet, user_data, msg) \
|
||||
|
@ -46,6 +46,21 @@ static const unsigned int _buffer_sizes[] = {
|
|||
static const unsigned int _buffer_sizes_count =
|
||||
sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
|
||||
|
||||
/* event dispatch helper */
|
||||
static void _event(struct libtelnet_t *telnet,
|
||||
enum libtelnet_event_type_t type, unsigned char command,
|
||||
unsigned char telopt, unsigned char *buffer, unsigned int size,
|
||||
void *user_data) {
|
||||
struct libtelnet_event_t ev;
|
||||
ev.type = type;
|
||||
ev.command = command;
|
||||
ev.telopt = telopt;
|
||||
ev.buffer = buffer;
|
||||
ev.size = size;
|
||||
|
||||
telnet->eh(telnet, &ev, user_data);
|
||||
}
|
||||
|
||||
/* error generation function */
|
||||
static void _error(struct libtelnet_t *telnet, const char *file, unsigned line,
|
||||
enum libtelnet_error_t err, void *user_data, const char *fmt, ...) {
|
||||
|
@ -61,13 +76,7 @@ static void _error(struct libtelnet_t *telnet, const char *file, unsigned line,
|
|||
fmt, va);
|
||||
va_end(va);
|
||||
|
||||
/* invoke user's custom error handler if possible; otherwise,
|
||||
* print to stderr
|
||||
*/
|
||||
if (telnet->cb->error)
|
||||
telnet->cb->error(telnet, err, buffer, user_data);
|
||||
else
|
||||
fprintf(stderr, "**ERROR**: libtelnet: %s\n", buffer);
|
||||
_event(telnet, LIBTELNET_EV_ERROR, err, 0, 0, 0, user_data);
|
||||
}
|
||||
|
||||
/* initialize the zlib box for a telnet box; if deflate is non-zero, it
|
||||
|
@ -105,10 +114,10 @@ z_stream *_init_zlib(struct libtelnet_t *telnet, int deflate,
|
|||
}
|
||||
|
||||
/* initialize a telnet state tracker */
|
||||
void libtelnet_init(struct libtelnet_t *telnet, struct libtelnet_cb_t *cb,
|
||||
void libtelnet_init(struct libtelnet_t *telnet, libtelnet_event_handler_t eh,
|
||||
enum libtelnet_mode_t mode) {
|
||||
memset(telnet, 0, sizeof(struct libtelnet_t));
|
||||
telnet->cb = cb;
|
||||
telnet->eh = eh;
|
||||
telnet->mode = mode;
|
||||
}
|
||||
|
||||
|
@ -188,8 +197,8 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
* switch states */
|
||||
if (byte == LIBTELNET_IAC) {
|
||||
if (i != start)
|
||||
telnet->cb->data(telnet, &buffer[start], i - start,
|
||||
user_data);
|
||||
_event(telnet, LIBTELNET_EV_DATA, 0, 0, &buffer[start],
|
||||
i - start, user_data);
|
||||
telnet->state = LIBTELNET_STATE_IAC;
|
||||
}
|
||||
break;
|
||||
|
@ -216,13 +225,13 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
break;
|
||||
/* IAC escaping */
|
||||
case LIBTELNET_IAC:
|
||||
telnet->cb->data(telnet, &byte, 1, user_data);
|
||||
_event(telnet, LIBTELNET_EV_DATA, 0, 0, &byte, 1, user_data);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
/* some other command */
|
||||
default:
|
||||
telnet->cb->command(telnet, byte, user_data);
|
||||
_event(telnet, LIBTELNET_EV_IAC, byte, 0, 0, 0, user_data);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
}
|
||||
|
@ -230,22 +239,26 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
|
||||
/* negotiation commands */
|
||||
case LIBTELNET_STATE_DO:
|
||||
telnet->cb->negotiate(telnet, LIBTELNET_DO, byte, user_data);
|
||||
_event(telnet, LIBTELNET_EV_NEGOTIATE, LIBTELNET_DO, byte,
|
||||
0, 0, user_data);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
case LIBTELNET_STATE_DONT:
|
||||
telnet->cb->negotiate(telnet, LIBTELNET_DONT, byte, user_data);
|
||||
_event(telnet, LIBTELNET_EV_NEGOTIATE, LIBTELNET_DONT, byte,
|
||||
0, 0, user_data);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
case LIBTELNET_STATE_WILL:
|
||||
telnet->cb->negotiate(telnet, LIBTELNET_WILL, byte, user_data);
|
||||
_event(telnet, LIBTELNET_EV_NEGOTIATE, LIBTELNET_WILL, byte,
|
||||
0, 0, user_data);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
case LIBTELNET_STATE_WONT:
|
||||
telnet->cb->negotiate(telnet, LIBTELNET_WONT, byte, user_data);
|
||||
_event(telnet, LIBTELNET_EV_NEGOTIATE, LIBTELNET_WONT, byte,
|
||||
0, 0, user_data);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
|
@ -280,8 +293,9 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
}
|
||||
|
||||
/* invoke callback */
|
||||
telnet->cb->subnegotiation(telnet, telnet->buffer[0],
|
||||
telnet->buffer + 1, telnet->length - 1, user_data);
|
||||
_event(telnet, LIBTELNET_EV_SUBNEGOTIATION, 0,
|
||||
telnet->buffer[0], telnet->buffer + 1,
|
||||
telnet->length - 1, user_data);
|
||||
telnet->length = 0;
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
|
@ -299,7 +313,8 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
break;
|
||||
|
||||
/* notify app that compression was enabled */
|
||||
telnet->cb->compress(telnet, 1, user_data);
|
||||
_event(telnet, LIBTELNET_EV_COMPRESS, 1, 0, 0, 0,
|
||||
user_data);
|
||||
|
||||
/* any remaining bytes in the buffer are compressed.
|
||||
* we have to re-invoke libtelnet_push to get those
|
||||
|
@ -340,7 +355,8 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
|
||||
/* pass through any remaining bytes */
|
||||
if (telnet->state == LIBTELNET_STATE_DATA && i != start)
|
||||
telnet->cb->data(telnet, &buffer[start], i - start, user_data);
|
||||
_event(telnet, LIBTELNET_EV_DATA, 0, 0, buffer + start, i - start,
|
||||
user_data);
|
||||
}
|
||||
|
||||
/* push a bytes into the state tracker */
|
||||
|
@ -378,7 +394,7 @@ void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
|
||||
/* on error (or on end of stream) disable further inflation */
|
||||
if (rs != Z_OK) {
|
||||
telnet->cb->compress(telnet, 0, user_data);
|
||||
_event(telnet, LIBTELNET_EV_COMPRESS, 0, 0, 0, 0, user_data);
|
||||
|
||||
inflateEnd(telnet->z_inflate);
|
||||
free(telnet->z_inflate);
|
||||
|
@ -418,8 +434,9 @@ static void _send(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
break;
|
||||
}
|
||||
|
||||
telnet->cb->send(telnet, deflate_buffer, sizeof(deflate_buffer) -
|
||||
telnet->z_deflate->avail_out, user_data);
|
||||
_event(telnet, LIBTELNET_EV_SEND, 0, 0, deflate_buffer,
|
||||
sizeof(deflate_buffer) - telnet->z_deflate->avail_out,
|
||||
user_data);
|
||||
|
||||
/* prepare output buffer for next run */
|
||||
telnet->z_deflate->next_out = deflate_buffer;
|
||||
|
@ -429,7 +446,7 @@ static void _send(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
/* COMPRESS2 is not negotiated, just send */
|
||||
} else
|
||||
#endif /* HAVE_ZLIB */
|
||||
telnet->cb->send(telnet, buffer, size, user_data);
|
||||
_event(telnet, LIBTELNET_EV_SEND, 0, 0, buffer, size, user_data);
|
||||
}
|
||||
|
||||
/* send an iac command */
|
||||
|
@ -489,7 +506,7 @@ void libtelnet_send_subnegotiation(struct libtelnet_t *telnet,
|
|||
return;
|
||||
|
||||
/* notify app that compression was enabled */
|
||||
telnet->cb->compress(telnet, 1, user_data);
|
||||
_event(telnet, LIBTELNET_EV_COMPRESS, 1, 0, 0, 0, user_data);
|
||||
}
|
||||
#endif /* HAVE_ZLIB */
|
||||
}
|
||||
|
|
57
libtelnet.h
57
libtelnet.h
|
@ -113,37 +113,38 @@ enum libtelnet_error_t {
|
|||
LIBTELNET_EUNKNOWN /* some crazy unexplainable unknown error */
|
||||
};
|
||||
|
||||
/* libtelnet callback declarations */
|
||||
struct libtelnet_cb_t {
|
||||
/* received (processed) data */
|
||||
void (*data)(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||
unsigned int size, void *user_data);
|
||||
/* processed data to buffer for sending */
|
||||
void (*send)(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||
unsigned int size, void *user_data);
|
||||
/* unknown command notification */
|
||||
void (*command)(struct libtelnet_t *telnet, unsigned char cmd,
|
||||
void *user_data);
|
||||
/* negotiation notification */
|
||||
void (*negotiate)(struct libtelnet_t *telnet, unsigned char cmd,
|
||||
unsigned char opt, void *user_data);
|
||||
/* unknown subnegotiation notification */
|
||||
void (*subnegotiation)(struct libtelnet_t *telnet, unsigned char opt,
|
||||
unsigned char *data, unsigned int size, void *user_data);
|
||||
/* error handler */
|
||||
void (*error)(struct libtelnet_t *telnet, enum libtelnet_error_t error,
|
||||
const char *msg, void *user_data);
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
void (*compress)(struct libtelnet_t *telnet,
|
||||
char enabled, void *user_data);
|
||||
#endif
|
||||
/* event codes */
|
||||
enum libtelnet_event_type_t {
|
||||
LIBTELNET_EV_DATA = 0,
|
||||
LIBTELNET_EV_SEND,
|
||||
LIBTELNET_EV_IAC,
|
||||
LIBTELNET_EV_NEGOTIATE,
|
||||
LIBTELNET_EV_SUBNEGOTIATION,
|
||||
LIBTELNET_EV_COMPRESS,
|
||||
LIBTELNET_EV_ERROR
|
||||
};
|
||||
|
||||
/* event information */
|
||||
struct libtelnet_event_t {
|
||||
/* type of event */
|
||||
enum libtelnet_event_type_t type;
|
||||
/* command info: only for IAC event */
|
||||
unsigned char command;
|
||||
/* telopt info: for NEGOTIATE and SUBNEGOTIATION events */
|
||||
unsigned char telopt;
|
||||
/* data buffer: for DATA, SEND, SUBNEGOTIATION, and ERROR events */
|
||||
unsigned char *buffer;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
/* event handler declaration */
|
||||
typedef void (*libtelnet_event_handler_t)(struct libtelnet_t *telnet,
|
||||
struct libtelnet_event_t *event, void *user_data);
|
||||
|
||||
/* state tracker */
|
||||
struct libtelnet_t {
|
||||
/* callback table */
|
||||
struct libtelnet_cb_t *cb;
|
||||
/* event handler */
|
||||
libtelnet_event_handler_t eh;
|
||||
#ifdef HAVE_ZLIB
|
||||
/* zlib (mccp2) compression */
|
||||
z_stream *z_deflate;
|
||||
|
@ -163,7 +164,7 @@ struct libtelnet_t {
|
|||
|
||||
/* initialize a telnet state tracker */
|
||||
extern void libtelnet_init(struct libtelnet_t *telnet,
|
||||
struct libtelnet_cb_t *cb, enum libtelnet_mode_t mode);
|
||||
libtelnet_event_handler_t eh, enum libtelnet_mode_t mode);
|
||||
|
||||
/* free up any memory allocated by a state tracker */
|
||||
extern void libtelnet_free(struct libtelnet_t *telnet);
|
||||
|
|
134
telnet-proxy.c
134
telnet-proxy.c
|
@ -127,32 +127,12 @@ static void print_buffer(unsigned char *buffer, unsigned int size) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _data_cb(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||
unsigned int size, void *user_data) {
|
||||
struct conn_t *conn = (struct conn_t*)user_data;
|
||||
|
||||
printf("%s DATA: ", conn->name);
|
||||
print_buffer(buffer, size);
|
||||
printf("\e[0m\n");
|
||||
|
||||
libtelnet_send_data(&conn->remote->telnet, buffer, size,
|
||||
conn->remote);
|
||||
}
|
||||
|
||||
static void _send_cb(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||
unsigned int size, void *user_data) {
|
||||
struct conn_t *conn = (struct conn_t*)user_data;
|
||||
static void _send(int sock, unsigned char *buffer, unsigned int size) {
|
||||
int rs;
|
||||
|
||||
/* DONT SPAM
|
||||
printf("%s SEND: ", conn->name);
|
||||
print_buffer(buffer, size);
|
||||
printf("\e[0m\n");
|
||||
*/
|
||||
|
||||
/* send data */
|
||||
while (size > 0) {
|
||||
if ((rs = send(conn->sock, buffer, size, 0)) == -1) {
|
||||
if ((rs = send(sock, buffer, size, 0)) == -1) {
|
||||
fprintf(stderr, "send() failed: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
} else if (rs == 0) {
|
||||
|
@ -166,55 +146,68 @@ static void _send_cb(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
}
|
||||
}
|
||||
|
||||
static void _command_cb(struct libtelnet_t *telnet, unsigned char cmd,
|
||||
void *user_data) {
|
||||
static void _event_handler(struct libtelnet_t *telnet,
|
||||
struct libtelnet_event_t *ev, void *user_data) {
|
||||
struct conn_t *conn = (struct conn_t*)user_data;
|
||||
|
||||
printf("%s IAC %s\e[0m\n", conn->name, get_cmd(cmd));
|
||||
switch (ev->type) {
|
||||
/* data received */
|
||||
case LIBTELNET_EV_DATA:
|
||||
printf("%s DATA: ", conn->name);
|
||||
print_buffer(ev->buffer, ev->size);
|
||||
printf("\e[0m\n");
|
||||
|
||||
libtelnet_send_command(&conn->remote->telnet, cmd, conn->remote);
|
||||
}
|
||||
libtelnet_send_data(&conn->remote->telnet, ev->buffer, ev->size,
|
||||
conn->remote);
|
||||
break;
|
||||
/* data must be sent */
|
||||
case LIBTELNET_EV_SEND:
|
||||
/* DONT SPAM
|
||||
printf("%s SEND: ", conn->name);
|
||||
print_buffer(ev->buffer, ev->size);
|
||||
printf("\e[0m\n");
|
||||
*/
|
||||
|
||||
static void _negotiate_cb(struct libtelnet_t *telnet, unsigned char cmd,
|
||||
unsigned char opt, void *user_data) {
|
||||
struct conn_t *conn = (struct conn_t*)user_data;
|
||||
_send(conn->sock, ev->buffer, ev->size);
|
||||
break;
|
||||
/* IAC command */
|
||||
case LIBTELNET_EV_IAC:
|
||||
printf("%s IAC %s\e[0m\n", conn->name, get_cmd(ev->command));
|
||||
|
||||
printf("%s IAC %s %d (%s)\e[0m\n", conn->name, get_cmd(cmd),
|
||||
(int)opt, get_opt(opt));
|
||||
libtelnet_send_command(&conn->remote->telnet, ev->command,
|
||||
conn->remote);
|
||||
break;
|
||||
/* negotiation */
|
||||
case LIBTELNET_EV_NEGOTIATE:
|
||||
printf("%s IAC %s %d (%s)\e[0m\n", conn->name, get_cmd(ev->command),
|
||||
(int)ev->telopt, get_opt(ev->telopt));
|
||||
|
||||
libtelnet_send_negotiate(&conn->remote->telnet, cmd, opt,
|
||||
conn->remote);
|
||||
}
|
||||
libtelnet_send_negotiate(&conn->remote->telnet, ev->command,
|
||||
ev->telopt, conn->remote);
|
||||
break;
|
||||
/* subnegotiation */
|
||||
case LIBTELNET_EV_SUBNEGOTIATION:
|
||||
printf("%s SUB %d (%s)", conn->name, (int)ev->telopt,
|
||||
get_opt(ev->telopt));
|
||||
if (ev->size > 0) {
|
||||
printf(" [%u]: ", ev->size);
|
||||
print_buffer(ev->buffer, ev->size);
|
||||
}
|
||||
printf("\e[0m\n");
|
||||
|
||||
static void _subnegotiation_cb(struct libtelnet_t *telnet,
|
||||
unsigned char type, unsigned char *buffer, unsigned int size,
|
||||
void *user_data) {
|
||||
struct conn_t *conn = (struct conn_t*)user_data;
|
||||
|
||||
printf("%s SUB %d (%s)", conn->name, (int)type, get_opt(type));
|
||||
if (size > 0) {
|
||||
printf(" [%u]: ", size);
|
||||
print_buffer(buffer, size);
|
||||
libtelnet_send_subnegotiation(&conn->remote->telnet, ev->telopt,
|
||||
ev->buffer, ev->size, conn->remote);
|
||||
break;
|
||||
/* compression notification */
|
||||
case LIBTELNET_EV_COMPRESS:
|
||||
printf("%s COMPRESSION %s\e[0m\n", conn->name,
|
||||
ev->command ? "ON" : "OFF");
|
||||
break;
|
||||
/* error */
|
||||
case LIBTELNET_EV_ERROR:
|
||||
printf("%s ERROR: %.*s\e[0m\n", conn->name, ev->size, ev->buffer);
|
||||
exit(1);
|
||||
}
|
||||
printf("\e[0m\n");
|
||||
|
||||
libtelnet_send_subnegotiation(&conn->remote->telnet, type, buffer, size,
|
||||
conn->remote);
|
||||
}
|
||||
|
||||
static void _compress_cb(struct libtelnet_t *telnet, char enabled,
|
||||
void *user_data) {
|
||||
struct conn_t *conn = (struct conn_t*)user_data;
|
||||
|
||||
printf("%s COMPRESSION %s\e[0m\n", conn->name, enabled ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
static void _error_cb(struct libtelnet_t *telnet, enum libtelnet_error_t error,
|
||||
const char *msg, void *user_data) {
|
||||
struct conn_t *conn = (struct conn_t*)user_data;
|
||||
|
||||
printf("%s ERROR: %s\e[0m\n", conn->name, msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
@ -226,7 +219,6 @@ int main(int argc, char **argv) {
|
|||
struct pollfd pfd[2];
|
||||
struct conn_t server;
|
||||
struct conn_t client;
|
||||
struct libtelnet_cb_t cb_table;
|
||||
struct addrinfo *ai;
|
||||
struct addrinfo hints;
|
||||
|
||||
|
@ -310,19 +302,9 @@ int main(int argc, char **argv) {
|
|||
client.name = "\e[34mCLIENT";
|
||||
client.remote = &server;
|
||||
|
||||
/* initialize libtelnet callback table */
|
||||
memset(&cb_table, 0, sizeof(cb_table));
|
||||
cb_table.data = _data_cb;
|
||||
cb_table.send = _send_cb;
|
||||
cb_table.command = _command_cb;
|
||||
cb_table.negotiate = _negotiate_cb;
|
||||
cb_table.subnegotiation = _subnegotiation_cb;
|
||||
cb_table.compress = _compress_cb;
|
||||
cb_table.error = _error_cb;
|
||||
|
||||
/* initialize telnet boxes */
|
||||
libtelnet_init(&server.telnet, &cb_table, LIBTELNET_MODE_PROXY);
|
||||
libtelnet_init(&client.telnet, &cb_table, LIBTELNET_MODE_PROXY);
|
||||
libtelnet_init(&server.telnet, _event_handler, LIBTELNET_MODE_PROXY);
|
||||
libtelnet_init(&client.telnet, _event_handler, LIBTELNET_MODE_PROXY);
|
||||
|
||||
/* initialize poll descriptors */
|
||||
memset(pfd, 0, sizeof(pfd));
|
||||
|
|
Loading…
Reference in New Issue