mirror of https://gerrit.osmocom.org/libtelnet
completed MCCP2 support
This commit is contained in:
parent
9de1598cd5
commit
61f8eb6556
110
libtelnet.c
110
libtelnet.c
|
@ -30,8 +30,9 @@ static const unsigned int _buffer_sizes_count =
|
||||||
sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
|
sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
|
||||||
|
|
||||||
/* initialize a telnet state tracker */
|
/* 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));
|
memset(telnet, 0, sizeof(struct libtelnet_t));
|
||||||
|
telnet->mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* free up any memory allocated by a state tracker */
|
/* 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->buffer + 1, telnet->length - 1, user_data);
|
||||||
telnet->length = 0;
|
telnet->length = 0;
|
||||||
|
|
||||||
/* if this was MCCP2, enable compression stream */
|
#ifdef HAVE_ZLIB
|
||||||
if (telnet->buffer[0] == LIBTELNET_OPTION_COMPRESS2) {
|
/* 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 */
|
/* allocate zstream box */
|
||||||
if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
|
if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
|
||||||
== 0) {
|
== 0) {
|
||||||
|
@ -222,6 +228,9 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* notify app that compression was enabled */
|
||||||
|
libtelnet_compress_cb(telnet, 1, user_data);
|
||||||
|
|
||||||
/* any remaining bytes in the buffer are compressed.
|
/* any remaining bytes in the buffer are compressed.
|
||||||
* we have to re-invoke libtelnet_push to get those
|
* we have to re-invoke libtelnet_push to get those
|
||||||
* bytes inflated and abort trying to process the
|
* bytes inflated and abort trying to process the
|
||||||
|
@ -232,6 +241,8 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||||
user_data);
|
user_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_ZLIB */
|
||||||
|
|
||||||
break;
|
break;
|
||||||
/* escaped IAC byte */
|
/* escaped IAC byte */
|
||||||
case LIBTELNET_IAC:
|
case LIBTELNET_IAC:
|
||||||
|
@ -264,10 +275,11 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||||
/* push a bytes into the state tracker */
|
/* push a bytes into the state tracker */
|
||||||
void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
|
void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||||
unsigned int size, void *user_data) {
|
unsigned int size, void *user_data) {
|
||||||
/* if COMPRESS2 has been negotiated and we have a zlib box, we
|
#ifdef HAVE_ZLIB
|
||||||
* need to inflate the buffer before processing
|
/* 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];
|
unsigned char inflate_buffer[4096];
|
||||||
int rs;
|
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 */
|
/* on error (or on end of stream) disable further inflation */
|
||||||
if (rs != Z_OK) {
|
if (rs != Z_OK) {
|
||||||
|
libtelnet_compress_cb(telnet, 0, user_data);
|
||||||
|
|
||||||
inflateEnd(telnet->zlib);
|
inflateEnd(telnet->zlib);
|
||||||
free(telnet->zlib);
|
free(telnet->zlib);
|
||||||
telnet->zlib = 0;
|
telnet->zlib = 0;
|
||||||
|
@ -305,24 +319,67 @@ void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* COMPRESS2 is not negotiated, just ignore */
|
/* COMPRESS2 is not negotiated, just process */
|
||||||
} else {
|
} else
|
||||||
|
#endif /* HAVE_ZLIB */
|
||||||
_process(telnet, buffer, size, user_data);
|
_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 */
|
/* send an iac command */
|
||||||
void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
|
void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
|
||||||
void *user_data) {
|
void *user_data) {
|
||||||
unsigned char bytes[2] = { LIBTELNET_IAC, cmd };
|
unsigned char bytes[2] = { LIBTELNET_IAC, cmd };
|
||||||
libtelnet_send_cb(telnet, bytes, 2, user_data);
|
_send(telnet, bytes, 2, user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send negotiation */
|
/* send negotiation */
|
||||||
void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
|
void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
|
||||||
unsigned char opt, void *user_data) {
|
unsigned char opt, void *user_data) {
|
||||||
unsigned char bytes[3] = { LIBTELNET_IAC, cmd, opt };
|
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) */
|
/* 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) {
|
if (buffer[i] == LIBTELNET_IAC) {
|
||||||
/* dump prior text if any */
|
/* dump prior text if any */
|
||||||
if (i != l)
|
if (i != l)
|
||||||
libtelnet_send_cb(telnet, buffer + l, i - l, user_data);
|
_send(telnet, buffer + l, i - l, user_data);
|
||||||
l = i + 1;
|
l = i + 1;
|
||||||
|
|
||||||
/* send escape */
|
/* send escape */
|
||||||
|
@ -344,7 +401,7 @@ void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer,
|
||||||
|
|
||||||
/* send whatever portion of buffer is left */
|
/* send whatever portion of buffer is left */
|
||||||
if (i != l)
|
if (i != l)
|
||||||
libtelnet_send_cb(telnet, buffer + l, i - l, user_data);
|
_send(telnet, buffer + l, i - l, user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send sub-request */
|
/* 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, &type, 1, user_data);
|
||||||
libtelnet_send_data(telnet, buffer, size, user_data);
|
libtelnet_send_data(telnet, buffer, size, user_data);
|
||||||
libtelnet_send_command(telnet, LIBTELNET_SE, 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 */
|
||||||
}
|
}
|
||||||
|
|
45
libtelnet.h
45
libtelnet.h
|
@ -28,6 +28,12 @@
|
||||||
#define LIBTELNET_OPTION_COMPRESS2 86
|
#define LIBTELNET_OPTION_COMPRESS2 86
|
||||||
#define LIBTELNET_OPTION_ZMP 93
|
#define LIBTELNET_OPTION_ZMP 93
|
||||||
|
|
||||||
|
/* libtelnet modes */
|
||||||
|
enum libtelnet_mode_t {
|
||||||
|
LIBTELNET_MODE_SERVER = 0,
|
||||||
|
LIBTELNET_MODE_CLIENT
|
||||||
|
};
|
||||||
|
|
||||||
/* telnet states */
|
/* telnet states */
|
||||||
enum libtelnet_state_t {
|
enum libtelnet_state_t {
|
||||||
LIBTELNET_STATE_DATA = 0,
|
LIBTELNET_STATE_DATA = 0,
|
||||||
|
@ -37,7 +43,7 @@ enum libtelnet_state_t {
|
||||||
LIBTELNET_STATE_WILL,
|
LIBTELNET_STATE_WILL,
|
||||||
LIBTELNET_STATE_WONT,
|
LIBTELNET_STATE_WONT,
|
||||||
LIBTELNET_STATE_SB,
|
LIBTELNET_STATE_SB,
|
||||||
LIBTELNET_STATE_SB_IAC,
|
LIBTELNET_STATE_SB_IAC
|
||||||
};
|
};
|
||||||
|
|
||||||
/* error codes */
|
/* error codes */
|
||||||
|
@ -46,7 +52,7 @@ enum libtelnet_error_t {
|
||||||
LIBTELNET_ERROR_NOMEM, /* memory allocation failure */
|
LIBTELNET_ERROR_NOMEM, /* memory allocation failure */
|
||||||
LIBTELNET_ERROR_OVERFLOW, /* data exceeds buffer size */
|
LIBTELNET_ERROR_OVERFLOW, /* data exceeds buffer size */
|
||||||
LIBTELNET_ERROR_PROTOCOL, /* invalid sequence of special bytes */
|
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 */
|
/* state tracker */
|
||||||
|
@ -63,50 +69,57 @@ struct libtelnet_t {
|
||||||
unsigned int length;
|
unsigned int length;
|
||||||
/* current state */
|
/* current state */
|
||||||
enum libtelnet_state_t state;
|
enum libtelnet_state_t state;
|
||||||
|
/* processing mode */
|
||||||
|
enum libtelnet_mode_t mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* libtelnet callback declarations
|
/* libtelnet callback declarations
|
||||||
* APPLICATION MUST IMPLEMENT THESE FUNCTIONS!!
|
* APPLICATION MUST IMPLEMENT THESE FUNCTIONS!!
|
||||||
*/
|
*/
|
||||||
extern void libtelnet_data_cb(struct libtelnet_t *telnet,
|
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,
|
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,
|
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,
|
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,
|
extern void libtelnet_subrequest_cb(struct libtelnet_t *telnet,
|
||||||
unsigned char type, unsigned char *data, unsigned int size,
|
unsigned char type, unsigned char *data, unsigned int size,
|
||||||
void *user_data);
|
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,
|
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 */
|
/* 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 */
|
/* free up any memory allocated by a state tracker */
|
||||||
extern void libtelnet_free(struct libtelnet_t *telnet);
|
extern void libtelnet_free(struct libtelnet_t *telnet);
|
||||||
|
|
||||||
/* push a byte buffer into the state tracker */
|
/* push a byte buffer into the state tracker */
|
||||||
extern void libtelnet_push(struct libtelnet_t *telnet,
|
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 */
|
/* send an iac command */
|
||||||
extern void libtelnet_send_command(struct libtelnet_t *telnet,
|
extern void libtelnet_send_command(struct libtelnet_t *telnet,
|
||||||
unsigned char cmd, void *user_data);
|
unsigned char cmd, void *user_data);
|
||||||
|
|
||||||
/* send negotiation */
|
/* send negotiation */
|
||||||
extern void libtelnet_send_negotiate(struct libtelnet_t *telnet,
|
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) */
|
/* send non-command data (escapes IAC bytes) */
|
||||||
extern void libtelnet_send_data(struct libtelnet_t *telnet,
|
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 */
|
/* send sub-request */
|
||||||
extern void libtelnet_send_subrequest(struct libtelnet_t *telnet,
|
extern void libtelnet_send_subrequest(struct libtelnet_t *telnet,
|
||||||
unsigned char type, unsigned char *buffer, unsigned int size,
|
unsigned char type, unsigned char *buffer, unsigned int size,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
#endif /* !defined(LIBTELNET_INCLUDE) */
|
#endif /* !defined(LIBTELNET_INCLUDE) */
|
||||||
|
|
|
@ -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),
|
printf("%s IAC %s %d (%s)\e[0m\n", conn->name, get_cmd(cmd),
|
||||||
(int)opt, get_opt(opt));
|
(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,
|
libtelnet_send_negotiate(&conn->remote->telnet, cmd, opt,
|
||||||
conn->remote);
|
conn->remote);
|
||||||
}
|
}
|
||||||
|
@ -209,6 +200,13 @@ void libtelnet_subrequest_cb(struct libtelnet_t *telnet, unsigned char type,
|
||||||
conn->remote);
|
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,
|
void libtelnet_error_cb(struct libtelnet_t *telnet,
|
||||||
enum libtelnet_error_t error, void *user_data) {
|
enum libtelnet_error_t error, void *user_data) {
|
||||||
struct conn_t *conn = (struct conn_t*)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.name = "\e[34mCLIENT";
|
||||||
client.remote = &server;
|
client.remote = &server;
|
||||||
|
|
||||||
/* initialize telnet boxes */
|
/* initialize telnet boxes
|
||||||
libtelnet_init(&server.telnet);
|
* NOTE: we set the server connect to the CLIENT mode because we
|
||||||
libtelnet_init(&client.telnet);
|
* 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 */
|
/* initialize poll descriptors */
|
||||||
memset(pfd, 0, sizeof(pfd));
|
memset(pfd, 0, sizeof(pfd));
|
||||||
|
|
Loading…
Reference in New Issue