mirror of https://gerrit.osmocom.org/libtelnet
completed MCCP2 support
This commit is contained in:
parent
9de1598cd5
commit
61f8eb6556
108
libtelnet.c
108
libtelnet.c
|
@ -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 */
|
||||
}
|
||||
|
|
19
libtelnet.h
19
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 @@ 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,6 +69,8 @@ struct libtelnet_t {
|
|||
unsigned int length;
|
||||
/* current state */
|
||||
enum libtelnet_state_t state;
|
||||
/* processing mode */
|
||||
enum libtelnet_mode_t mode;
|
||||
};
|
||||
|
||||
/* libtelnet callback declarations
|
||||
|
@ -79,11 +87,16 @@ extern void libtelnet_negotiate_cb(struct libtelnet_t *telnet,
|
|||
extern void libtelnet_subrequest_cb(struct libtelnet_t *telnet,
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue