completed MCCP2 support

This commit is contained in:
Sean Middleditch 2009-03-14 04:57:27 -04:00
parent 9de1598cd5
commit 61f8eb6556
3 changed files with 140 additions and 41 deletions

View File

@ -30,8 +30,9 @@ static const unsigned int _buffer_sizes_count =
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 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
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 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
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 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
user_data);
return;
}
#endif /* HAVE_ZLIB */
break;
/* escaped IAC byte */
case LIBTELNET_IAC:
@ -264,10 +275,11 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
/* 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 @@ void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
/* 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 @@ void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
}
}
/* 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 @@ void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer,
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 @@ void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer,
/* 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 @@ void libtelnet_send_subrequest(struct libtelnet_t *telnet, unsigned char type,
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 */
}

View File

@ -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 @@ enum libtelnet_state_t {
LIBTELNET_STATE_WILL,
LIBTELNET_STATE_WONT,
LIBTELNET_STATE_SB,
LIBTELNET_STATE_SB_IAC,
LIBTELNET_STATE_SB_IAC
};
/* error codes */
@ -46,7 +52,7 @@ enum libtelnet_error_t {
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 @@ struct libtelnet_t {
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) */

View File

@ -181,15 +181,6 @@ void libtelnet_negotiate_cb(struct libtelnet_t *telnet, unsigned char cmd,
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 @@ void libtelnet_subrequest_cb(struct libtelnet_t *telnet, unsigned char type,
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 @@ int main(int argc, char **argv) {
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));