force client into character mode
This commit is contained in:
parent
b1b990d861
commit
9c6269218e
5 changed files with 200 additions and 16 deletions
2
build.sh
2
build.sh
|
@ -7,6 +7,6 @@
|
||||||
name="moxerver"
|
name="moxerver"
|
||||||
|
|
||||||
include_dir="."
|
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
|
gcc $src_list -o $name -I $include_dir
|
||||||
|
|
43
client.c
43
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));
|
fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno));
|
||||||
return -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...
|
//TODO let's print received bytes during development phase...
|
||||||
if (!strncmp(client->data, "QUIT", 4)) {
|
{
|
||||||
client_close(client);
|
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...
|
/* handle special telnet characters coming from client */
|
||||||
fprintf(stderr, "client %s says: %s", client->ip_string, client->data);
|
telnet_handle_client_read(client->data, &len);
|
||||||
|
|
||||||
return 0;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sends data from a buffer to client. */
|
/* Sends data from a buffer to client. */
|
||||||
int client_write(struct client_t *client, char *databuf, int datalen) {
|
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 */
|
/* 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));
|
fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno));
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
15
moxerver.c
15
moxerver.c
|
@ -2,12 +2,12 @@
|
||||||
#include <unistd.h> /* getopt() */
|
#include <unistd.h> /* 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_MIN 4001 /* minimum TCP port number */
|
||||||
#define PORT_MAX 4008 /* maximum TCP port number */
|
#define PORT_MAX 4008 /* maximum TCP port number */
|
||||||
|
|
||||||
/* Prints help message. */
|
/* Prints help message. */
|
||||||
void usage() {
|
static void usage() {
|
||||||
//TODO maybe some styling should be done
|
//TODO maybe some styling should be done
|
||||||
fprintf(stderr, "Usage: moxerver -p tcp_port -t tty_path [-h]\n");
|
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);
|
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
|
//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) {
|
while (1) {
|
||||||
|
|
||||||
/* setup parameters for select() */
|
/* setup parameters for select() */
|
||||||
|
@ -114,6 +114,8 @@ int main(int argc, char *argv[]) {
|
||||||
fprintf(stderr, "problem accepting client\n");
|
fprintf(stderr, "problem accepting client\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
/* put client in "character" mode */
|
||||||
|
telnet_set_character_mode(&client);
|
||||||
}
|
}
|
||||||
/* reject connection request if a client is already connected */
|
/* reject connection request if a client is already connected */
|
||||||
else {
|
else {
|
||||||
|
@ -124,12 +126,15 @@ int main(int argc, char *argv[]) {
|
||||||
if ( (client.socket != -1) && FD_ISSET(client.socket, &read_fds) ) {
|
if ( (client.socket != -1) && FD_ISSET(client.socket, &read_fds) ) {
|
||||||
/* read client data */
|
/* read client data */
|
||||||
ret = client_read(&client);
|
ret = client_read(&client);
|
||||||
if ( ret != 0) {
|
if ( ret < 0) {
|
||||||
/* print error but continue waiting for new data */
|
/* print error but continue waiting for new data */
|
||||||
fprintf(stderr, "problem reading client\n");
|
fprintf(stderr, "problem reading client\n");
|
||||||
continue;
|
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) {
|
if (ret == 0) {
|
||||||
|
|
11
moxerver.h
11
moxerver.h
|
@ -51,6 +51,14 @@ int client_read(struct client_t *client);
|
||||||
int client_write(struct client_t *client, char *databuf, int datalen);
|
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. */
|
/* Opens the tty device and configures it. */
|
||||||
int tty_open(struct tty_t *tty_dev, char* path);
|
int tty_open(struct tty_t *tty_dev, char* path);
|
||||||
/* Closes the tty device. */
|
/* 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);
|
int tty_read(struct tty_t *tty_dev);
|
||||||
/* Sends data from a buffer to tty device. */
|
/* Sends data from a buffer to tty device. */
|
||||||
int tty_write(struct tty_t *tty_dev, char *databuf, int datalen);
|
int tty_write(struct tty_t *tty_dev, char *databuf, int datalen);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
145
telnet.c
Normal file
145
telnet.c
Normal file
|
@ -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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue