scrap the multiple callbacks thing, just use a single event handler callback
diff --git a/README b/README
index 62a1893..07fa52c 100644
--- a/README
+++ b/README
@@ -49,8 +49,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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
=====================================================================
diff --git a/libtelnet.c b/libtelnet.c
index 0c3496b..755ebed 100644
--- a/libtelnet.c
+++ b/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_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 @@
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 @@
}
/* 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 @@
* 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 @@
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 @@
/* 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 @@
}
/* 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 @@
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 @@
/* 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 @@
/* 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 @@
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 @@
/* 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 @@
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 */
}
diff --git a/libtelnet.h b/libtelnet.h
index 4efc87b..843b76b 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -113,37 +113,38 @@
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 @@
/* 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);
diff --git a/telnet-proxy.c b/telnet-proxy.c
index ca808a7..935abe6 100644
--- a/telnet-proxy.c
+++ b/telnet-proxy.c
@@ -127,32 +127,12 @@
}
}
-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 _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 @@
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 @@
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));