/* * Handling details related to telnet protocol. */ #include "moxerver.h" /* structure for holding telnet option name and value */ typedef struct { const char *name; char value; } telnet_option_t; /* supported telnet option values */ telnet_option_t telnet_options[] = { {"WILL", 251}, {"WONT", 252}, {"DO", 253}, {"DONT", 254}, {"IAC", 255}, {"ECHO", 1}, {"SGA", 3}, {"LINEMODE", 34}, {NULL, 0} /* this list must end with {NULL, 0} */ }; /* Returns telnet option name based on the value. */ static const char* telnet_option_name(int value) { int i = 0; while (telnet_options[i].name) { if (telnet_options[i].value == value) return telnet_options[i].name; i++; } return '\0'; } /* Returns telnet option value based on the name. */ static char telnet_option_value(const char* name) { int i = 0; while (telnet_options[i].name) { if (!strcmp(telnet_options[i].name, name)) return telnet_options[i].value; i++; } return 0; } /* Sends telnet option command. Returns 0 on success, -1 on failure. */ static int telnet_send_command(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. Always returns 0.*/ static int telnet_handle_command(char *databuf, int datalen) { /* currently just print received commands because we set the client, we don't adapt to client commands */ 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(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. * Used to filter out handshake commands of telnet protocol. */ int telnet_handle_client_read(char *databuf, int *datalen) { int i; char newdata[DATABUF_LEN]; 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. * Used for echoing characters correctly to telnet client. */ int telnet_handle_client_write(char *databuf, int *datalen) { int i; char newdata[DATABUF_LEN]; 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; }