From 9c6269218e86639cc8c473f38f04ec37c4faa0e0 Mon Sep 17 00:00:00 2001 From: socec Date: Sat, 8 Mar 2014 18:26:04 +0100 Subject: [PATCH] force client into character mode --- build.sh | 2 +- client.c | 43 ++++++++++++---- moxerver.c | 15 ++++-- moxerver.h | 11 ++++ telnet.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+), 16 deletions(-) create mode 100644 telnet.c diff --git a/build.sh b/build.sh index 7c8ca4a..4731ab7 100755 --- a/build.sh +++ b/build.sh @@ -7,6 +7,6 @@ name="moxerver" include_dir="." -src_list="$name.c server.c client.c tty.c" +src_list="$name.c server.c client.c tty.c telnet.c" gcc $src_list -o $name -I $include_dir diff --git a/client.c b/client.c index 750294b..4d2d5c0 100644 --- a/client.c +++ b/client.c @@ -23,26 +23,49 @@ int client_read(struct client_t *client) { fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); return -errno; } - /* null-terminate received data */ - client->data[len] = '\0'; - //TODO how does a client disconnect? For now let's wait for QUIT command... - if (!strncmp(client->data, "QUIT", 4)) { - client_close(client); + //TODO let's print received bytes during development phase... + { + int i; + for(i = 0; i < len; i++) { + fprintf(stderr, "client %s <- %u '%c'\n", + client->ip_string, + (unsigned char) client->data[i], + (unsigned char) client->data[i]); + } } - //TODO let's print the data during development phase... - fprintf(stderr, "client %s says: %s", client->ip_string, client->data); + /* handle special telnet characters coming from client */ + telnet_handle_client_read(client->data, &len); - return 0; + return len; } /* Sends data from a buffer to client. */ int client_write(struct client_t *client, char *databuf, int datalen) { + + int len; + + /* handle special telnet characters to display them correctly on client */ + telnet_handle_client_write(databuf, &datalen); + + //TODO let's print received bytes during development phase... + { + int i; + for(i = 0; i < len; i++) { + fprintf(stderr, "client %s -> %u '%c'\n", + client->ip_string, + (unsigned char) databuf[i], + (unsigned char) databuf[i]); + } + } + /* send data to client */ - if (send(client->socket, databuf, datalen, 0) == -1) { + len = send(client->socket, databuf, datalen, 0); + if (len == -1) { fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); return -errno; } - return 0; + + return len; } diff --git a/moxerver.c b/moxerver.c index e5ae418..fa5c20e 100644 --- a/moxerver.c +++ b/moxerver.c @@ -2,12 +2,12 @@ #include /* getopt() */ -#define SERVER_WAIT_TIMEOUT 5 /* seconds for select() timeout in server loop */ +#define SERVER_WAIT_TIMEOUT 10 /* seconds for select() timeout in server loop */ #define PORT_MIN 4001 /* minimum TCP port number */ #define PORT_MAX 4008 /* maximum TCP port number */ /* Prints help message. */ -void usage() { +static void usage() { //TODO maybe some styling should be done fprintf(stderr, "Usage: moxerver -p tcp_port -t tty_path [-h]\n"); fprintf(stderr, "- tcp_port range [%d .. %d]\n\n", PORT_MIN, PORT_MAX); @@ -82,7 +82,7 @@ int main(int argc, char *argv[]) { //TODO this is a good place to create and start the TTY thread, use "tty_path" when opening device - /* loop with timeouts waiting for client connection */ + /* loop with timeouts waiting for client connection and data*/ while (1) { /* setup parameters for select() */ @@ -114,6 +114,8 @@ int main(int argc, char *argv[]) { fprintf(stderr, "problem accepting client\n"); continue; } + /* put client in "character" mode */ + telnet_set_character_mode(&client); } /* reject connection request if a client is already connected */ else { @@ -124,12 +126,15 @@ int main(int argc, char *argv[]) { if ( (client.socket != -1) && FD_ISSET(client.socket, &read_fds) ) { /* read client data */ ret = client_read(&client); - if ( ret != 0) { + if ( ret < 0) { /* print error but continue waiting for new data */ fprintf(stderr, "problem reading client\n"); continue; } - //TODO we should send this data to TTY device + /* echo back to client */ + client_write(&client, client.data, ret); + + //TODO we should send this data to TTY device here } } if (ret == 0) { diff --git a/moxerver.h b/moxerver.h index a24f0fc..155088e 100644 --- a/moxerver.h +++ b/moxerver.h @@ -51,6 +51,14 @@ int client_read(struct client_t *client); int client_write(struct client_t *client, char *databuf, int datalen); +/* Tells client to go into "character" mode. */ +int telnet_set_character_mode(struct client_t *client); +/* Handles special characters in data buffer after receiving them from client. */ +int telnet_handle_client_read(char *databuf, int *datalen); +/* Handles special characters in data buffer before sending to client. */ +int telnet_handle_client_write(char *databuf, int *datalen); + + /* Opens the tty device and configures it. */ int tty_open(struct tty_t *tty_dev, char* path); /* Closes the tty device. */ @@ -61,3 +69,6 @@ int tty_reconfigure(struct tty_t *tty_dev, struct termios newttyset); int tty_read(struct tty_t *tty_dev); /* Sends data from a buffer to tty device. */ int tty_write(struct tty_t *tty_dev, char *databuf, int datalen); + + + diff --git a/telnet.c b/telnet.c new file mode 100644 index 0000000..f3b8c80 --- /dev/null +++ b/telnet.c @@ -0,0 +1,145 @@ +#include "moxerver.h" + + +/* structure for holding telnet option name and value */ +struct telnet_option_t { + const char *name; + char value; +}; + +/* supported telnet option values */ +struct telnet_option_t telnet_options[] = { + {"WILL", 251}, + {"WONT", 252}, + {"DO", 253}, + {"DONT", 254}, + {"IAC", 255}, + {"ECHO", 1}, + {"SGA", 3}, + {"LINEMODE", 34}, + {"SLE", 45} +}; +#define TELNET_OPTIONS_COUNT 9 /* keep this up with the number of supported options */ + +/* Returns telnet option name based on the value. */ +static const char* telnet_option_name(int value) { + int i; + for (i = 0; i < TELNET_OPTIONS_COUNT; i ++) + if (telnet_options[i].value == value) + return telnet_options[i].name; + return '\0'; +} + +/* Returns telnet option value based on the name. */ +static char telnet_option_value(const char* name) { + int i; + for (i = 0; i < TELNET_OPTIONS_COUNT; i ++) + if (!strcmp(telnet_options[i].name, name)) + return telnet_options[i].value; + return 0; +} + +/* Sends telnet option command. */ +int telnet_send_command(struct client_t *client, const char* option, const char* command) { + + char data[3]; + + /* pack command */ + data[0] = telnet_option_value("IAC"); + data[1] = telnet_option_value(option); + data[2] = telnet_option_value(command); + + /* send command */ + if (client_write(client, data, 3) <= 0) { + fprintf(stderr, "[%s:%d] failed sending %s %s\n", __func__, __LINE__, option, command); + return -1; + } + + fprintf(stderr, "[%s]: sent %s %s\n", __func__, option, command); + return 0; +} + +/* Handles received telnet option command. */ +int telnet_handle_command(char *databuf, int datalen) { + + if (databuf[0] == telnet_option_value("IAC")) { + fprintf(stderr, "[%s]: received %s %s\n", __func__, + telnet_option_name(databuf[1]), telnet_option_name(databuf[2])); + } + + return 0; +} + +/* Tells client to go into "character" mode. */ +int telnet_set_character_mode(struct client_t *client) { + + int err = 0; + + /* send predefined commands and add up their return vaules */ + err += telnet_send_command(client, "WILL", "ECHO"); + err += telnet_send_command(client, "WILL", "SGA"); + err += telnet_send_command(client, "WONT", "LINEMODE"); /* this depends on telnet client */ + + //TODO Do we verify client response? What do we do if the response is not how we expected? + + return err; +} + +/* Handles special characters in data buffer after receiving them from client. */ +int telnet_handle_client_read(char *databuf, int *datalen) { + + int i; + char newdata[DATA_BUFLEN]; + int newlen = 0; + + /* process data using a new buffer */ + for (i = 0; i < *datalen; i++) { + /* handle and discard telnet commands */ + if (databuf[i] == telnet_option_value("IAC")) { + telnet_handle_command((databuf+i), 3); + i += 2; + } + /* let other data pass through */ + else { + newdata[newlen++] = databuf[i]; + } + } + /* overwrite data with new buffer */ + for (i = 0; i < newlen; i++) databuf[i] = newdata[i]; + *datalen = newlen; + + return 0; +} + +/* Handles special characters in data buffer before sending to client. */ +int telnet_handle_client_write(char *databuf, int *datalen) { + + int i; + char newdata[DATA_BUFLEN]; + int newlen = 0; + + /* process data using a new buffer */ + for (i = 0; i < *datalen; i++) { + /* pressed ENTER */ + if (databuf[i] == 13) { + fprintf(stderr, "[%s]: handling ENTER\n", __func__); + newdata[newlen++] = '\r'; + newdata[newlen++] = '\n'; + } + /* pressed BACKSPACE */ + if (databuf[i] == 127) { + fprintf(stderr, "[%s]: handling BACKSPACE\n", __func__); + newdata[newlen++] = 8; + newdata[newlen++] = ' '; + newdata[newlen++] = 8; + } + else { + newdata[newlen++] = databuf[i]; + } + } + /* overwrite data with new buffer */ + for (i = 0; i < newlen; i++) databuf[i] = newdata[i]; + *datalen = newlen; + + return 0; +}