make use of a callback table instead of undefined functions for integration
diff --git a/README b/README
index 97c84e7..c3bd6de 100644
--- a/README
+++ b/README
@@ -50,13 +50,7 @@
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
-libtelnet_*_cb() functions.
-
-The libtelnet_*_cb() functions (callback fuctions from here out) are
-not actually implemented by libtelnet, but are used by it. The
-libtelnet.h header includes declarations for the functions but it is
-the application's responsibility to provide implementations of these
-functions.
+callback structure libtelnet_cb_t.
IIa. Initialization
@@ -66,13 +60,22 @@
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,
- enum libtelnet_mode_t mode);
+ 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
@@ -96,8 +99,9 @@
IIc. Sending Data
Note that all of the libtelnet_send_*() functions will invoke
- the libtelnet_send_cb() callback function. The user_data parameter
- to each of these functions is passed through to the callback.
+ 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);
@@ -124,12 +128,39 @@
IId. Callbacks
- The application is responsible for implementing these functions.
- The function definitions are included in libtelnet.h. If any of
- these functions are not implemented and linked into the application
- then libtelnet.o will generate linker errors for undefined symbols.
+ 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.
- void libtelnet_data_cb(struct libtelnet_t *telnet,
+ 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
@@ -141,7 +172,7 @@
line-based processing of data, it is your responsibility to
buffer data and find the line breaks.
- void libtelnet_send_cb(struct libtelnet_t *telnet,
+ 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
@@ -152,14 +183,14 @@
parameter to libtelnet calls so that it is available in this
callback.
- void libtelnet_command_cb(struct libtelnet_t *telnet,
+ 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_negotiate_cb(struct libtelnet_t *telnet,
+ 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
@@ -171,7 +202,7 @@
http://www.faqs.org/rfcs/rfc1143.html
- void libtelnet_subnegotiation_cb(struct libtelnet_t *telnet,
+ 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.
@@ -183,7 +214,7 @@
The opt parameter is the option under sub-negotiation. The
remaining data (if any) is passed in the buffer.
- void libtelnet_compress_cb(struct libtelnet_t *telnet,
+ 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
diff --git a/libtelnet.c b/libtelnet.c
index 0a4a9f0..544c0ef 100644
--- a/libtelnet.c
+++ b/libtelnet.c
@@ -30,8 +30,10 @@
sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
/* initialize a telnet state tracker */
-void libtelnet_init(struct libtelnet_t *telnet, enum libtelnet_mode_t mode) {
+void libtelnet_init(struct libtelnet_t *telnet, struct libtelnet_cb_t *cb,
+ enum libtelnet_mode_t mode) {
memset(telnet, 0, sizeof(struct libtelnet_t));
+ telnet->cb = cb;
telnet->mode = mode;
}
@@ -72,7 +74,7 @@
/* overflow -- can't grow any more */
if (i >= _buffer_sizes_count - 1) {
- libtelnet_error_cb(telnet, LIBTELNET_ERROR_OVERFLOW, user_data);
+ telnet->cb->error(telnet, LIBTELNET_ERROR_OVERFLOW, user_data);
libtelnet_free(telnet);
return LIBTELNET_ERROR_OVERFLOW;
}
@@ -81,7 +83,7 @@
new_buffer = (unsigned char *)realloc(telnet->buffer,
_buffer_sizes[i + 1]);
if (new_buffer == 0) {
- libtelnet_error_cb(telnet, LIBTELNET_ERROR_NOMEM,
+ telnet->cb->error(telnet, LIBTELNET_ERROR_NOMEM,
user_data);
libtelnet_free(telnet);
return LIBTELNET_ERROR_NOMEM;
@@ -109,7 +111,7 @@
* switch states */
if (byte == LIBTELNET_IAC) {
if (i != start)
- libtelnet_data_cb(telnet, &buffer[start], i - start,
+ telnet->cb->data(telnet, &buffer[start], i - start,
user_data);
telnet->state = LIBTELNET_STATE_IAC;
}
@@ -137,13 +139,13 @@
break;
/* IAC escaping */
case LIBTELNET_IAC:
- libtelnet_data_cb(telnet, &byte, 1, user_data);
+ telnet->cb->data(telnet, &byte, 1, user_data);
start = i + 1;
telnet->state = LIBTELNET_STATE_DATA;
break;
/* some other command */
default:
- libtelnet_command_cb(telnet, byte, user_data);
+ telnet->cb->command(telnet, byte, user_data);
start = i + 1;
telnet->state = LIBTELNET_STATE_DATA;
}
@@ -151,22 +153,22 @@
/* negotiation commands */
case LIBTELNET_STATE_DO:
- libtelnet_negotiate_cb(telnet, LIBTELNET_DO, byte, user_data);
+ telnet->cb->negotiate(telnet, LIBTELNET_DO, byte, user_data);
start = i + 1;
telnet->state = LIBTELNET_STATE_DATA;
break;
case LIBTELNET_STATE_DONT:
- libtelnet_negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data);
+ telnet->cb->negotiate(telnet, LIBTELNET_DONT, byte, user_data);
start = i + 1;
telnet->state = LIBTELNET_STATE_DATA;
break;
case LIBTELNET_STATE_WILL:
- libtelnet_negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data);
+ telnet->cb->negotiate(telnet, LIBTELNET_WILL, byte, user_data);
start = i + 1;
telnet->state = LIBTELNET_STATE_DATA;
break;
case LIBTELNET_STATE_WONT:
- libtelnet_negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data);
+ telnet->cb->negotiate(telnet, LIBTELNET_WONT, byte, user_data);
start = i + 1;
telnet->state = LIBTELNET_STATE_DATA;
break;
@@ -195,13 +197,13 @@
/* zero-size buffer is a protocol error */
if (telnet->length == 0) {
- libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
+ telnet->cb->error(telnet, LIBTELNET_ERROR_PROTOCOL,
user_data);
break;
}
/* invoke callback */
- libtelnet_subnegotiation_cb(telnet, telnet->buffer[0],
+ telnet->cb->subnegotiation(telnet, telnet->buffer[0],
telnet->buffer + 1, telnet->length - 1, user_data);
telnet->length = 0;
@@ -215,7 +217,7 @@
/* allocate zstream box */
if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
== 0) {
- libtelnet_error_cb(telnet,
+ telnet->cb->error(telnet,
LIBTELNET_ERROR_NOMEM, user_data);
}
@@ -224,13 +226,13 @@
if (inflateInit(telnet->zlib) != Z_OK) {
free(telnet->zlib);
telnet->zlib = 0;
- libtelnet_error_cb(telnet,
+ telnet->cb->error(telnet,
LIBTELNET_ERROR_UNKNOWN, user_data);
break;
}
/* notify app that compression was enabled */
- libtelnet_compress_cb(telnet, 1, user_data);
+ telnet->cb->compress(telnet, 1, user_data);
/* any remaining bytes in the buffer are compressed.
* we have to re-invoke libtelnet_push to get those
@@ -258,7 +260,7 @@
break;
/* something else -- protocol error */
default:
- libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
+ telnet->cb->error(telnet, LIBTELNET_ERROR_PROTOCOL,
user_data);
start = i + 1;
telnet->state = LIBTELNET_STATE_DATA;
@@ -270,7 +272,7 @@
/* pass through any remaining bytes */
if (telnet->state == LIBTELNET_STATE_DATA && i != start)
- libtelnet_data_cb(telnet, &buffer[start], i - start, user_data);
+ telnet->cb->data(telnet, &buffer[start], i - start, user_data);
}
/* push a bytes into the state tracker */
@@ -302,7 +304,7 @@
_process(telnet, inflate_buffer, sizeof(inflate_buffer) -
telnet->zlib->avail_out, user_data);
else
- libtelnet_error_cb(telnet, LIBTELNET_ERROR_UNKNOWN,
+ telnet->cb->error(telnet, LIBTELNET_ERROR_UNKNOWN,
user_data);
/* prepare output buffer for next run */
@@ -311,7 +313,7 @@
/* on error (or on end of stream) disable further inflation */
if (rs != Z_OK) {
- libtelnet_compress_cb(telnet, 0, user_data);
+ telnet->cb->compress(telnet, 0, user_data);
inflateEnd(telnet->zlib);
free(telnet->zlib);
@@ -347,7 +349,7 @@
/* compress */
if (deflate(telnet->zlib, Z_SYNC_FLUSH) != Z_OK) {
- libtelnet_error_cb(telnet, LIBTELNET_ERROR_UNKNOWN,
+ telnet->cb->error(telnet, LIBTELNET_ERROR_UNKNOWN,
user_data);
deflateEnd(telnet->zlib);
free(telnet->zlib);
@@ -355,7 +357,7 @@
break;
}
- libtelnet_send_cb(telnet, deflate_buffer, sizeof(deflate_buffer) -
+ telnet->cb->send(telnet, deflate_buffer, sizeof(deflate_buffer) -
telnet->zlib->avail_out, user_data);
/* prepare output buffer for next run */
@@ -366,7 +368,7 @@
/* COMPRESS2 is not negotiated, just send */
} else
#endif /* HAVE_ZLIB */
- libtelnet_send_cb(telnet, buffer, size, user_data);
+ telnet->cb->send(telnet, buffer, size, user_data);
}
/* send an iac command */
@@ -423,7 +425,7 @@
/* allocate zstream box */
if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
== 0) {
- libtelnet_error_cb(telnet,
+ telnet->cb->error(telnet,
LIBTELNET_ERROR_NOMEM, user_data);
}
@@ -432,12 +434,12 @@
if (deflateInit(telnet->zlib, Z_DEFAULT_COMPRESSION) != Z_OK) {
free(telnet->zlib);
telnet->zlib = 0;
- libtelnet_error_cb(telnet,
+ telnet->cb->error(telnet,
LIBTELNET_ERROR_UNKNOWN, user_data);
}
/* notify app that compression was enabled */
- libtelnet_compress_cb(telnet, 1, user_data);
+ telnet->cb->compress(telnet, 1, user_data);
}
#endif /* HAVE_ZLIB */
}
diff --git a/libtelnet.h b/libtelnet.h
index 729bd50..821839f 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -12,6 +12,10 @@
#if !defined(LIBTELNET_INCLUDE)
#define LIBTELNET_INCLUDE 1
+/* forward declarations */
+struct libtelnet_t;
+struct libtelnet_cb_t;
+
/* telnet special values */
#define LIBTELNET_IAC 255
#define LIBTELNET_DONT 254
@@ -108,10 +112,40 @@
LIBTELNET_ERROR_UNKNOWN /* 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, void *user_data);
+
+ #ifdef HAVE_ZLIB
+ void (*compress)(struct libtelnet_t *telnet,
+ char enabled, void *user_data);
+ #endif
+};
+
/* state tracker */
struct libtelnet_t {
- /* zlib (mccp2) compression */
+ /* callback table */
+ struct libtelnet_cb_t *cb;
#ifdef HAVE_ZLIB
+ /* zlib (mccp2) compression */
z_stream *zlib;
#endif
/* sub-request buffer */
@@ -126,30 +160,9 @@
enum libtelnet_mode_t mode;
};
-/* libtelnet callback declarations
- * APPLICATION MUST IMPLEMENT THESE FUNCTIONS!!
- */
-extern void libtelnet_data_cb(struct libtelnet_t *telnet,
- unsigned char *buffer, unsigned int size, void *user_data);
-extern void libtelnet_send_cb(struct libtelnet_t *telnet,
- unsigned char *buffer, unsigned int size, void *user_data);
-extern void libtelnet_command_cb(struct libtelnet_t *telnet,
- unsigned char cmd, void *user_data);
-extern void libtelnet_negotiate_cb(struct libtelnet_t *telnet,
- unsigned char cmd, unsigned char opt, void *user_data);
-extern void libtelnet_subnegotiation_cb(struct libtelnet_t *telnet,
- unsigned char opt, unsigned char *data, unsigned int size,
- void *user_data);
-#ifdef HAVE_ZLIB
-extern void libtelnet_compress_cb(struct libtelnet_t *telnet,
- char enabled, void *user_data);
-#endif
-extern void libtelnet_error_cb(struct libtelnet_t *telnet,
- enum libtelnet_error_t error, void *user_data);
-
/* initialize a telnet state tracker */
extern void libtelnet_init(struct libtelnet_t *telnet,
- enum libtelnet_mode_t mode);
+ struct libtelnet_cb_t *cb, 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 96a1fe3..8179253 100644
--- a/telnet-proxy.c
+++ b/telnet-proxy.c
@@ -126,7 +126,7 @@
}
}
-void libtelnet_data_cb(struct libtelnet_t *telnet, unsigned char *buffer,
+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;
@@ -138,7 +138,7 @@
conn->remote);
}
-void libtelnet_send_cb(struct libtelnet_t *telnet, unsigned char *buffer,
+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;
int rs;
@@ -165,7 +165,7 @@
}
}
-void libtelnet_command_cb(struct libtelnet_t *telnet, unsigned char cmd,
+static void _command_cb(struct libtelnet_t *telnet, unsigned char cmd,
void *user_data) {
struct conn_t *conn = (struct conn_t*)user_data;
@@ -174,7 +174,7 @@
libtelnet_send_command(&conn->remote->telnet, cmd, conn->remote);
}
-void libtelnet_negotiate_cb(struct libtelnet_t *telnet, unsigned char cmd,
+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;
@@ -185,7 +185,7 @@
conn->remote);
}
-void libtelnet_subnegotiation_cb(struct libtelnet_t *telnet,
+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;
@@ -201,14 +201,14 @@
conn->remote);
}
-void libtelnet_compress_cb(struct libtelnet_t *telnet, char enabled,
+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");
}
-void libtelnet_error_cb(struct libtelnet_t *telnet,
+static void _error_cb(struct libtelnet_t *telnet,
enum libtelnet_error_t error, void *user_data) {
struct conn_t *conn = (struct conn_t*)user_data;
@@ -225,6 +225,7 @@
struct pollfd pfd[2];
struct conn_t server;
struct conn_t client;
+ struct libtelnet_cb_t cb_table;
/* check usage */
if (argc != 4) {
@@ -298,13 +299,23 @@
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
* NOTE: we set the server connect to the CLIENT mode because we
* are acting as a client of the server; likewise, we set the
* client connection to SERVER mode becauser we are acting as a
* server to the client. */
- libtelnet_init(&server.telnet, LIBTELNET_MODE_CLIENT);
- libtelnet_init(&client.telnet, LIBTELNET_MODE_SERVER);
+ libtelnet_init(&server.telnet, &cb_table, LIBTELNET_MODE_CLIENT);
+ libtelnet_init(&client.telnet, &cb_table, LIBTELNET_MODE_SERVER);
/* initialize poll descriptors */
memset(pfd, 0, sizeof(pfd));