From 291448511463e7ca7bc55198d448a2d3ecd5de21 Mon Sep 17 00:00:00 2001 From: Sean Middleditch Date: Thu, 12 Mar 2009 23:14:47 -0400 Subject: [PATCH] initial commit --- libtelnet.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++ libtelnet.h | 111 +++++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 libtelnet.c create mode 100644 libtelnet.h diff --git a/libtelnet.c b/libtelnet.c new file mode 100644 index 0000000..b148d8e --- /dev/null +++ b/libtelnet.c @@ -0,0 +1,162 @@ +#include "libtelnet.h" + +/* initialize a telnet state tracker */ +void libtelnet_init(struct libtelnet_t *telnet) { + telnet->state = LIBTELNET_TEXT; + telnet->buffer = 0; + telnet->size = 0; + telnet->length = 0; +} + +/* free up any memory allocated by a state tracker */ +void libtelnet_close(struct libtelnet_t *telnet) { + if (telnet->buffer) { + free(telnet->buffer); + telnet->buffer = 0; + } +} + +/* push a single byte into the state tracker */ +void libtelnet_push_byte(struct libtelnet_t *telnet, unsigned char byte, + void *user_data) { + switch (telnet->state) { + /* regular data */ + case LIBTELNET_STATE_TEXT: + /* enter IAC state on IAC byte */ + if (byte == LIBTELNET_IAC) + telnet->state = LIBTELNET_STATE_IAC; + /* regular input byte */ + else + telnet->input_cb(telnet, byte, user_data); + break; + case LIBTELNET_STATE_IAC: + switch (byte) { + /* subrequest */ + case LIBTELNET_SB: + telnet->state = LIBTELNET_STATE_SB; + break; + /* negotiation commands */ + case LIBTELNET_WILL: + telnet->state = LIBTELNET_STATE_WILL; + break; + case LIBTELNET_WONT: + telnet->state = LIBTELNET_STATE_WONT; + break; + case LIBTELNET_DO: + telnet->state = LIBTELNET_STATE_DO; + break; + case LIBTELNET_DONT: + telnet->state = LIBTELNET_STATE_DONT; + break; + /* IAC escaping */ + case LIBTELNET_IAC: + telbet->input_cb(telnet, LIBTELNET_IAC, user_data); + libtelnet->state = LIBTELNET_STATE_TEXT; + break; + /* some other command */ + default: + telnet->command_cb(telnet, byte, user_data); + telnet->state = LIBTELNET_STATE_TEXT; + } + break; + /* DO negotiation */ + case LIBTELNET_STATE_DO: + telnet->negotiate_cb(telnet, LIBTELNET_DO, byte, user_data); + telnet->state = LIBTELNET_STATE_TEXT; + break; + case LIBTELNET_STATE_DONT: + telnet->negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data); + telnet->state = LIBTELNET_STATE_TEXT; + break; + case LIBTELNET_STATE_WILL: + telnet->negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data); + telnet->state = LIBTELNET_STATE_TEXT; + break; + case LIBTELNET_STATE_WONT: + telnet->negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data); + telnet->state = LIBTELNET_STATE_TEXT; + break; + /* subrequest -- buffer bytes until end request */ + case LIBTELNET_STATE_SB: + /* IAC command in subrequest -- either IAC SE or IAC IAC */ + if (byte == LIBTELNET_IAC) { + telnet->state = LIBTELNET_STATE_SB_IAC; + } else { + /* FIXME: buffer byte */ + } + break; + /* IAC escaping inside a subrequest */ + case LIBTELNET_STATE_SB_IAC: + switch (byte) { + /* end subrequest */ + case LIBTELNET_SE: + /* FIXME: process */ + telnet->state = LIBTELNET_STATE_TEXT; + break; + /* escaped IAC byte */ + case LIBTELNET_IAC: + /* FIXME: buffer byte */ + telnet->state = LIBTELNET_STATE_SB; + break; + /* something else -- protocol error */ + default: + telnet->error_cb(telnet, LIBTELNET_ERROR_PROTOCOL, user_data); + telnet->state = LIBTELNET_STATE_TEXT; + break; + } + break; + } +} + +/* push a byte buffer into the state tracker */ +void libtelnet_push_buffer(struct libtelnet_t *telnet, unsigned char *buffer, + size_t size, void *user_data) { + size_t i; + for (i = 0; i != size; ++i) + libtelnet_push_byte(telnet, buffer[i], user_data); +} + +/* send an iac command */ +void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd, + void *user_data) { + unsigned char bytes[2] = { IAC, cmd }; + telnet->output_cb(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] = { IAC, cmd, opt }; + telnet->output_cb(telnet, bytes, 3, user_data); +} + +/* send non-command data (escapes IAC bytes) */ +void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer, + size_t size, void *user_data) { + size_t i, l; + for (l = i = 0; i != size; ++i) { + /* dump prior portion of text, send escaped bytes */ + if (buffer[i] == LIBTELNET_IAC) { + /* dump prior text if any */ + if (i != l) + telnet->output_cb(telnet, buffer + l, i - l, user_data); + l = i + 1; + + /* send escape */ + libtelnet_send_command(telnet, LIBTELNET_IAC, user_data); + } + } + + /* send whatever portion of buffer is left */ + if (i != l) + telnet->output_cb(telnet, buffer + l, i - l, user_data); +} + +/* send sub-request */ +void libtelnet_send_subrequest(struct libtelnet_t *telnet, unsigned char type, + unsigned char *buffer, size_t size, void *user_data) { + libtelnet_send_command(telnet, SB, user_data); + libtelnet_send_data(telnet, &type, 1, user_data); + libtelnet_send_data(telnet, buffer, size, user_data); + libtelnet_send_command(telnet, SE, user_data); +} diff --git a/libtelnet.h b/libtelnet.h new file mode 100644 index 0000000..a3cba24 --- /dev/null +++ b/libtelnet.h @@ -0,0 +1,111 @@ +/* + * The author or authors of this code dedicate any and all copyright interest + * in this code to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and successors. We + * intend this dedication to be an overt act of relinquishment in perpetuity of + * all present and future rights to this code under copyright law. + */ + +/* sub request buffer size increment (defualt 4K) */ +#define LIBTELNET_BUFFER_SIZE (4 * 1024) +/* sub request buffer size (default 16K) */ +#define LIBTELNET_BUFFER_SIZE_MAX (16 * 1024) + +/* telnet special values */ +#define LIBTELNET_IAC 255 +#define LIBTELNET_DONT 254 +#define LIBTELNET_DO 253 +#define LIBTELNET_WONT 252 +#define LIBTELNET_WILL 251 +#define LIBTELNET_SB 250 +#define LIBTELNET_SE 240 + +/* telnet options */ +#define LIBTELNET_OPTION_BINARY 0 +#define LIBTELNET_OPTION_ECHO 1 +#define LIBTELNET_OPTION_NAWS 31 +#define LIBTELNET_OPTION_ZMP 93 + +/* telnet states */ +enum libtelnet_state_t { + LIBTELNET_STATE_TEXT = 0, + LIBTELNET_STATE_IAC, + LIBTELNET_STATE_DO, + LIBTELNET_STATE_DONT, + LIBTELNET_STATE_WILL, + LIBTELNET_STATE_WONT, + LIBTELNET_STATE_SB, + LIBTELNET_STATE_SB_IAC, +}; + +/* error codes */ +enum libtelnet_error_t { + LIBTELNET_ERROR_OK = 0, + LIBTELNET_ERROR_OVERFLOW, /* input exceeds buffer size */ + LIBTELNET_ERROR_PROTOCOL, /* invalid sequence of special bytes */ + LIBTELNET_ERROR_UNKNOWN, /* some crazy unexplainable unknown error */ +}; + +/* callback prototypes */ +typedef (void)(*libtelnet_input)(struct libtelnet_t *telnet, unsigned char + byte, void *user_data); +typedef (void)(*libtelnet_output)(struct libtelnet_t *telnet, unsigned char + byte, void *user_data); +typedef (void)(*libtelnet_command)(struct libtelnet_t *telnet, unsigned char + cmd, void *user_data); +typedef (void)(*libtelnet_negotiate)(struct libtelnet_t *telnet, unsigned char + cmd, unsigned char opt, void *user_data); +typedef (void)(*libtelnet_subrequest)(struct libtelnet_t *telnet, unsigned char + cmd, unsigned char type, unsigned char *data, size_t size, + void *user_data); +typedef (void)(*libtelnet_error)(struct libtelnet_t *telnet, + enum libtelnet_error_t error, void *user_data); + +/* state tracker */ +struct libtelnet_t { + /* current state */ + enum libtelnet_state_t state; + /* sub-request buffer */ + char *buffer; + /* current size of the buffer */ + size_t size; + /* length of data in the buffer */ + size_t length; + + /* callbacks */ + libtelnet_input input_cb; + libtelnet_output output_cb; + libtelnet_command command_cb; + libtelnet_negotiate negotiate_cb; + libtelnet_subrequest subrequest_cb; +}; + +/* initialize a telnet state tracker */ +void libtelnet_init(struct libtelnet_t *telnet); + +/* free up any memory allocated by a state tracker */ +void libtelnet_close(struct libtelnet_t *telnet); + +/* push a single byte into the state tracker */ +void libtelnet_push_byte(struct libtelnet_t *telnet, unsigned char byte, + void *user_data); + +/* push a byte buffer into the state tracker */ +void libtelnet_push_buffer(struct libtelnet_t *telnet, unsigned char *buffer, + size_t size, void *user_data); + +/* send an iac command */ +void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd, + void *user_data); + +/* send negotiation */ +void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd, + unsigned char opt, void *user_data); + +/* send non-command data (escapes IAC bytes) */ +void libtelnet_send_data(struct libtelnet_t *telnet, unsigned char *buffer, + size_t size, void *user_data); + +/* send sub-request */ +void libtelnet_send_subrequest(struct libtelnet_t *telnet, unsigned char type, + unsigned char *buffer, size_t size, void *user_data);