completed MCCP2 support
diff --git a/libtelnet.c b/libtelnet.c
index 37b7b9b..b7a3aed 100644
--- a/libtelnet.c
+++ b/libtelnet.c
@@ -30,8 +30,9 @@
sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
/* initialize a telnet state tracker */
-void libtelnet_init(struct libtelnet_t *telnet) {
+void libtelnet_init(struct libtelnet_t *telnet, enum libtelnet_mode_t mode) {
memset(telnet, 0, sizeof(struct libtelnet_t));
+ telnet->mode = mode;
}
/* free up any memory allocated by a state tracker */
@@ -203,8 +204,13 @@
telnet->buffer + 1, telnet->length - 1, user_data);
telnet->length = 0;
- /* if this was MCCP2, enable compression stream */
- if (telnet->buffer[0] == LIBTELNET_OPTION_COMPRESS2) {
+#ifdef HAVE_ZLIB
+ /* if we are a client and just received the COMPRESS2
+ * begin marker, setup our zlib box and start handling
+ * the compressed stream
+ */
+ if (telnet->mode == LIBTELNET_MODE_CLIENT &&
+ telnet->buffer[0] == LIBTELNET_OPTION_COMPRESS2) {
/* allocate zstream box */
if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
== 0) {
@@ -222,6 +228,9 @@
break;
}
+ /* notify app that compression was enabled */
+ libtelnet_compress_cb(telnet, 1, user_data);
+
/* any remaining bytes in the buffer are compressed.
* we have to re-invoke libtelnet_push to get those
* bytes inflated and abort trying to process the
@@ -232,6 +241,8 @@
user_data);
return;
}
+#endif /* HAVE_ZLIB */
+
break;
/* escaped IAC byte */
case LIBTELNET_IAC:
@@ -264,10 +275,11 @@
/* push a bytes into the state tracker */
void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
unsigned int size, void *user_data) {
- /* if COMPRESS2 has been negotiated and we have a zlib box, we
- * need to inflate the buffer before processing
+#ifdef HAVE_ZLIB
+ /* if we are a client and we have a zlib box, then COMPRESS2 has been
+ * negotiated and we need to inflate the buffer before processing
*/
- if (telnet->zlib) {
+ if (telnet->mode == LIBTELNET_MODE_CLIENT && telnet->zlib != 0) {
unsigned char inflate_buffer[4096];
int rs;
@@ -298,6 +310,8 @@
/* on error (or on end of stream) disable further inflation */
if (rs != Z_OK) {
+ libtelnet_compress_cb(telnet, 0, user_data);
+
inflateEnd(telnet->zlib);
free(telnet->zlib);
telnet->zlib = 0;
@@ -305,24 +319,67 @@
}
}
- /* COMPRESS2 is not negotiated, just ignore */
- } else {
+ /* COMPRESS2 is not negotiated, just process */
+ } else
+#endif /* HAVE_ZLIB */
_process(telnet, buffer, size, user_data);
- }
+}
+
+static void _send(struct libtelnet_t *telnet, unsigned char *buffer,
+ unsigned int size, void *user_data) {
+#ifdef HAVE_ZLIB
+ /* if we are a server and we have a zlib box, then COMPRESS2 has been
+ * negotiated and we need to deflate the buffer before sending it out
+ */
+ if (telnet->mode == LIBTELNET_MODE_SERVER && telnet->zlib != 0) {
+ unsigned char deflate_buffer[1024];
+
+ /* initialize zlib state */
+ telnet->zlib->next_in = buffer;
+ telnet->zlib->avail_in = size;
+ telnet->zlib->next_out = deflate_buffer;
+ telnet->zlib->avail_out = sizeof(deflate_buffer);
+
+ /* deflate until buffer exhausted and all output is produced */
+ while (telnet->zlib->avail_in > 0 || telnet->zlib->avail_out == 0) {
+ /* reset output buffer */
+
+ /* compress */
+ if (deflate(telnet->zlib, Z_SYNC_FLUSH) != Z_OK) {
+ libtelnet_error_cb(telnet, LIBTELNET_ERROR_UNKNOWN,
+ user_data);
+ deflateEnd(telnet->zlib);
+ free(telnet->zlib);
+ telnet->zlib = 0;
+ break;
+ }
+
+ libtelnet_send_cb(telnet, deflate_buffer, sizeof(deflate_buffer) -
+ telnet->zlib->avail_out, user_data);
+
+ /* prepare output buffer for next run */
+ telnet->zlib->next_out = deflate_buffer;
+ telnet->zlib->avail_out = sizeof(deflate_buffer);
+ }
+
+ /* COMPRESS2 is not negotiated, just send */
+ } else
+#endif /* HAVE_ZLIB */
+ libtelnet_send_cb(telnet, buffer, size, user_data);
}
/* send an iac command */
void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
void *user_data) {
unsigned char bytes[2] = { LIBTELNET_IAC, cmd };
- libtelnet_send_cb(telnet, bytes, 2, user_data);
+ _send(telnet, bytes, 2, user_data);
}
/* send negotiation */
void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
unsigned char opt, void *user_data) {
unsigned char bytes[3] = { LIBTELNET_IAC, cmd, opt };
- libtelnet_send_cb(telnet, bytes, 3, user_data);
+ _send(telnet, bytes, 3, user_data);
}
/* send non-command data (escapes IAC bytes) */
@@ -334,7 +391,7 @@
if (buffer[i] == LIBTELNET_IAC) {
/* dump prior text if any */
if (i != l)
- libtelnet_send_cb(telnet, buffer + l, i - l, user_data);
+ _send(telnet, buffer + l, i - l, user_data);
l = i + 1;
/* send escape */
@@ -344,7 +401,7 @@
/* send whatever portion of buffer is left */
if (i != l)
- libtelnet_send_cb(telnet, buffer + l, i - l, user_data);
+ _send(telnet, buffer + l, i - l, user_data);
}
/* send sub-request */
@@ -354,4 +411,31 @@
libtelnet_send_data(telnet, &type, 1, user_data);
libtelnet_send_data(telnet, buffer, size, user_data);
libtelnet_send_command(telnet, LIBTELNET_SE, user_data);
+
+#ifdef HAVE_ZLIB
+ /* if we're a server and we just sent the COMPRESS2 marker, we must
+ * make sure all further data is compressed
+ */
+ if (telnet->mode == LIBTELNET_MODE_SERVER && type ==
+ LIBTELNET_OPTION_COMPRESS2) {
+ /* allocate zstream box */
+ if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
+ == 0) {
+ libtelnet_error_cb(telnet,
+ LIBTELNET_ERROR_NOMEM, user_data);
+ }
+
+ /* initialize */
+ memset(telnet->zlib, 0, sizeof(z_stream));
+ if (deflateInit(telnet->zlib, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ free(telnet->zlib);
+ telnet->zlib = 0;
+ libtelnet_error_cb(telnet,
+ LIBTELNET_ERROR_UNKNOWN, user_data);
+ }
+
+ /* notify app that compression was enabled */
+ libtelnet_compress_cb(telnet, 1, user_data);
+ }
+#endif /* HAVE_ZLIB */
}
diff --git a/libtelnet.h b/libtelnet.h
index 1ad8e3d..575d7ba 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -28,6 +28,12 @@
#define LIBTELNET_OPTION_COMPRESS2 86
#define LIBTELNET_OPTION_ZMP 93
+/* libtelnet modes */
+enum libtelnet_mode_t {
+ LIBTELNET_MODE_SERVER = 0,
+ LIBTELNET_MODE_CLIENT
+};
+
/* telnet states */
enum libtelnet_state_t {
LIBTELNET_STATE_DATA = 0,
@@ -37,7 +43,7 @@
LIBTELNET_STATE_WILL,
LIBTELNET_STATE_WONT,
LIBTELNET_STATE_SB,
- LIBTELNET_STATE_SB_IAC,
+ LIBTELNET_STATE_SB_IAC
};
/* error codes */
@@ -46,7 +52,7 @@
LIBTELNET_ERROR_NOMEM, /* memory allocation failure */
LIBTELNET_ERROR_OVERFLOW, /* data exceeds buffer size */
LIBTELNET_ERROR_PROTOCOL, /* invalid sequence of special bytes */
- LIBTELNET_ERROR_UNKNOWN, /* some crazy unexplainable unknown error */
+ LIBTELNET_ERROR_UNKNOWN /* some crazy unexplainable unknown error */
};
/* state tracker */
@@ -63,50 +69,57 @@
unsigned int length;
/* current state */
enum libtelnet_state_t state;
+ /* processing mode */
+ 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);
+ 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);
+ unsigned char *buffer, unsigned int size, void *user_data);
extern void libtelnet_command_cb(struct libtelnet_t *telnet,
- unsigned char cmd, void *user_data);
+ unsigned char cmd, void *user_data);
extern void libtelnet_negotiate_cb(struct libtelnet_t *telnet,
- unsigned char cmd, unsigned char opt, void *user_data);
+ unsigned char cmd, unsigned char opt, void *user_data);
extern void libtelnet_subrequest_cb(struct libtelnet_t *telnet,
- unsigned char type, unsigned char *data, unsigned int size,
- void *user_data);
+ unsigned char type, 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);
+ enum libtelnet_error_t error, void *user_data);
/* initialize a telnet state tracker */
-extern void libtelnet_init(struct libtelnet_t *telnet);
+extern void libtelnet_init(struct libtelnet_t *telnet,
+ enum libtelnet_mode_t mode);
/* free up any memory allocated by a state tracker */
extern void libtelnet_free(struct libtelnet_t *telnet);
/* push a byte buffer into the state tracker */
extern void libtelnet_push(struct libtelnet_t *telnet,
- unsigned char *buffer, unsigned int size, void *user_data);
+ unsigned char *buffer, unsigned int size, void *user_data);
/* send an iac command */
extern void libtelnet_send_command(struct libtelnet_t *telnet,
- unsigned char cmd, void *user_data);
+ unsigned char cmd, void *user_data);
/* send negotiation */
extern void libtelnet_send_negotiate(struct libtelnet_t *telnet,
- unsigned char cmd, unsigned char opt, void *user_data);
+ unsigned char cmd, unsigned char opt, void *user_data);
/* send non-command data (escapes IAC bytes) */
extern void libtelnet_send_data(struct libtelnet_t *telnet,
- unsigned char *buffer, unsigned int size, void *user_data);
+ unsigned char *buffer, unsigned int size, void *user_data);
/* send sub-request */
extern void libtelnet_send_subrequest(struct libtelnet_t *telnet,
- unsigned char type, unsigned char *buffer, unsigned int size,
- void *user_data);
+ unsigned char type, unsigned char *buffer, unsigned int size,
+ void *user_data);
#endif /* !defined(LIBTELNET_INCLUDE) */
diff --git a/telnet-proxy.c b/telnet-proxy.c
index 9a38af6..218d860 100644
--- a/telnet-proxy.c
+++ b/telnet-proxy.c
@@ -181,15 +181,6 @@
printf("%s IAC %s %d (%s)\e[0m\n", conn->name, get_cmd(cmd),
(int)opt, get_opt(opt));
- /* FIXME: HACK: allow MCCP2 from server without passing it
- * through to client. this is temporary until libtelnet supports
- * the server-end of MCCP2.
- */
- if (cmd == LIBTELNET_WILL && opt == LIBTELNET_OPTION_COMPRESS2) {
- libtelnet_send_negotiate(&conn->telnet, LIBTELNET_DO, opt, conn);
- return;
- }
-
libtelnet_send_negotiate(&conn->remote->telnet, cmd, opt,
conn->remote);
}
@@ -209,6 +200,13 @@
conn->remote);
}
+void libtelnet_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,
enum libtelnet_error_t error, void *user_data) {
struct conn_t *conn = (struct conn_t*)user_data;
@@ -296,9 +294,13 @@
client.name = "\e[34mCLIENT";
client.remote = &server;
- /* initialize telnet boxes */
- libtelnet_init(&server.telnet);
- libtelnet_init(&client.telnet);
+ /* 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);
/* initialize poll descriptors */
memset(pfd, 0, sizeof(pfd));