diff -c orig-src/comm.c src/comm.c *** orig-src/comm.c Wed Nov 13 21:02:18 2002 --- src/comm.c Sat Dec 2 10:06:44 2006 *************** *** 60,70 **** --- 60,78 ---- #include "db.h" #include "house.h" + #ifdef DEBUG + # define TELCMDS + # define SLC_NAMES // LINEMODE options + #endif + #define TELOPTS #ifdef HAVE_ARPA_TELNET_H #include #else #include "telnet.h" #endif + #ifndef NTELOPTS + # define NTELOPTS (TELOPT_NEW_ENVIRON + 1) + #endif #ifndef INVALID_SOCKET #define INVALID_SOCKET (-1) *************** *** 176,181 **** --- 184,227 ---- #endif + /* flags for telnet protocol */ + #define TELNET_SEND_BINARY (1 << 0) /* we may send binary data */ + #define TELNET_RECV_BINARY (1 << 1) /* we may receive binary data */ + #define TELNET_SEND_EOR (1 << 2) /* we should send EOR */ + #define TELNET_ECHO (1 << 3) /* we handle echoing */ + #define TELNET_ECHO_OFF (1 << 4) /* echoing is off */ + #define TELNET_SUPGA (1 << 5) /* supress sending of GA */ + + /* data structure used for telnet protocol handling (RFC854, etc.) */ + struct telnet_data + { + int tnt_sentWILL[NTELOPTS]; + int tnt_sentWONT[NTELOPTS]; + int tnt_sentDO [NTELOPTS]; + int tnt_sentDONT[NTELOPTS]; + + bitvector_t tnt_flags; /* see the TELNET_* flags above */ + }; + + static int send_will(struct descriptor_data *d, int opt, int r); + static int send_wont(struct descriptor_data *d, int opt, int r); + static int send_do(struct descriptor_data *d, int opt, int r); + static int send_dont(struct descriptor_data *d, int opt, int r); + static int telnet_new_connection(struct descriptor_data *d); + static void telnet_handle_input(struct descriptor_data *d, char *buf, + const size_t length); + static void telnet_handle_output(struct descriptor_data *d, char *dest, + const char *buf, const size_t length); + + #ifdef DEBUG + /* set these to TRUE to send all telnet sequences to the log */ + int log_telnet_input = TRUE; + int log_telnet_output = TRUE; + #endif + + static size_t write_to_output_2(struct descriptor_data *t, + char *txt, size_t size); + /*********************************************************************** * main game loop and related stuff * ***********************************************************************/ *************** *** 767,773 **** /* Print prompts for other descriptors who had no other output */ for (d = descriptor_list; d; d = d->next) { if (!d->has_prompt && d->bufptr == 0) { ! write_to_descriptor(d->descriptor, make_prompt(d)); d->has_prompt = TRUE; } } --- 813,820 ---- /* Print prompts for other descriptors who had no other output */ for (d = descriptor_list; d; d = d->next) { if (!d->has_prompt && d->bufptr == 0) { ! char *prompt = make_prompt(d); ! write_to_descriptor(d->descriptor, prompt, strlen(prompt)); d->has_prompt = TRUE; } } *************** *** 954,959 **** --- 1001,1007 ---- */ void echo_off(struct descriptor_data *d) { + /* char off_string[] = { (char) IAC, *************** *** 963,968 **** --- 1011,1019 ---- }; write_to_output(d, "%s", off_string); + */ + + send_will(d, TELOPT_ECHO, FALSE); } *************** *** 971,976 **** --- 1022,1028 ---- */ void echo_on(struct descriptor_data *d) { + /* char on_string[] = { (char) IAC, *************** *** 980,985 **** --- 1032,1040 ---- }; write_to_output(d, "%s", on_string); + */ + + send_wont(d, TELOPT_ECHO, FALSE); } *************** *** 1119,1128 **** size_t wantsize; int size; - /* if we're in the overflow state already, ignore this new output */ - if (t->bufspace == 0) - return (0); - wantsize = size = vsnprintf(txt, sizeof(txt), format, args); /* If exceeding the size of the buffer, truncate it for the overflow message */ if (size < 0 || wantsize >= sizeof(txt)) { --- 1174,1179 ---- *************** *** 1130,1135 **** --- 1181,1196 ---- strcpy(txt + size - strlen(text_overflow), text_overflow); /* strcpy: OK */ } + return write_to_output_2(t, txt, strlen(txt)); + } + + static size_t write_to_output_2(struct descriptor_data *t, + char *txt, size_t size) + { + /* if we're in the overflow state already, ignore this new output */ + if (t->bufspace == 0) + return (0); + /* * If the text is too big to fit into even a large buffer, truncate * the new text to make it fit. (This will switch to the overflow *************** *** 1146,1152 **** * text just barely fits, then it's switched to a large buffer instead. */ if (t->bufspace > size) { ! strcpy(t->output + t->bufptr, txt); /* strcpy: OK (size checked above) */ t->bufspace -= size; t->bufptr += size; return (t->bufspace); --- 1207,1214 ---- * text just barely fits, then it's switched to a large buffer instead. */ if (t->bufspace > size) { ! memcpy(t->output + t->bufptr, txt, size); ! //strcpy(t->output + t->bufptr, txt); /* strcpy: OK (size checked above) */ t->bufspace -= size; t->bufptr += size; return (t->bufspace); *************** *** 1312,1318 **** sockets_connected++; if (sockets_connected >= max_players) { ! write_to_descriptor(desc, "Sorry, CircleMUD is full right now... please try again later!\r\n"); CLOSE_SOCKET(desc); return (0); } --- 1374,1381 ---- sockets_connected++; if (sockets_connected >= max_players) { ! static const char *full_message = "Sorry, CircleMUD is full right now... please try again later!\r\n"; ! write_to_descriptor(desc, full_message, strlen(full_message)); CLOSE_SOCKET(desc); return (0); } *************** *** 1362,1367 **** --- 1425,1438 ---- newd->has_prompt = 1; /* prompt is part of greetings */ STATE(newd) = CON_GET_NAME; + if (!telnet_new_connection(newd)) + { + CLOSE_SOCKET(newd->descriptor); + free(newd); + return (0); + } + + /* * This isn't exactly optimal but allows us to make a design choice. * Do we embed the history in descriptor_data or keep it dynamically *************** *** 1395,1419 **** */ int process_output(struct descriptor_data *t) { ! char i[MAX_SOCK_BUF], *osb = i + 2; int result; /* we may need this \r\n for later -- see below */ strcpy(i, "\r\n"); /* strcpy: OK (for 'MAX_SOCK_BUF >= 3') */ /* now, append the 'real' output */ ! strcpy(osb, t->output); /* strcpy: OK (t->output:LARGE_BUFSIZE < osb:MAX_SOCK_BUF-2) */ /* if we're in the overflow state, notify the user */ if (t->bufspace == 0) ! strcat(osb, "**OVERFLOW**\r\n"); /* strcpy: OK (osb:MAX_SOCK_BUF-2 reserves space) */ /* add the extra CRLF if the person isn't in compact mode */ if (STATE(t) == CON_PLAYING && t->character && !IS_NPC(t->character) && !PRF_FLAGGED(t->character, PRF_COMPACT)) ! strcat(osb, "\r\n"); /* strcpy: OK (osb:MAX_SOCK_BUF-2 reserves space) */ /* add a prompt */ ! strcat(i, make_prompt(t)); /* strcpy: OK (i:MAX_SOCK_BUF reserves space) */ /* * now, send the output. If this is an 'interruption', use the prepended --- 1466,1491 ---- */ int process_output(struct descriptor_data *t) { ! char i[MAX_SOCK_BUF * 2], *osb = i + 2; int result; /* we may need this \r\n for later -- see below */ strcpy(i, "\r\n"); /* strcpy: OK (for 'MAX_SOCK_BUF >= 3') */ /* now, append the 'real' output */ ! telnet_handle_output(t, osb, t->output, t->bufptr); ! //strcpy(osb, t->output); /* strcpy: OK (t->output:LARGE_BUFSIZE < osb:MAX_SOCK_BUF-2) */ /* if we're in the overflow state, notify the user */ if (t->bufspace == 0) ! strcat(osb + t->bufptr, "**OVERFLOW**\r\n"); /* strcpy: OK (osb:MAX_SOCK_BUF-2 reserves space) */ /* add the extra CRLF if the person isn't in compact mode */ if (STATE(t) == CON_PLAYING && t->character && !IS_NPC(t->character) && !PRF_FLAGGED(t->character, PRF_COMPACT)) ! strcat(osb + t->bufptr, "\r\n"); /* strcpy: OK (osb:MAX_SOCK_BUF-2 reserves space) */ /* add a prompt */ ! strcat(i + t->bufptr + 2, make_prompt(t)); /* strcpy: OK (i:MAX_SOCK_BUF reserves space) */ /* * now, send the output. If this is an 'interruption', use the prepended *************** *** 1421,1431 **** */ if (t->has_prompt) { t->has_prompt = FALSE; ! result = write_to_descriptor(t->descriptor, i); if (result >= 2) result -= 2; } else ! result = write_to_descriptor(t->descriptor, osb); if (result < 0) { /* Oops, fatal error. Bye! */ close_socket(t); --- 1493,1503 ---- */ if (t->has_prompt) { t->has_prompt = FALSE; ! result = write_to_descriptor(t->descriptor, i, t->bufptr + 2); if (result >= 2) result -= 2; } else ! result = write_to_descriptor(t->descriptor, osb, t->bufptr); if (result < 0) { /* Oops, fatal error. Bye! */ close_socket(t); *************** *** 1434,1441 **** --- 1506,1516 ---- return (0); /* Handle snooping: prepend "% " and send to snooper. */ + #if 0 if (t->snoop_by) + // TODO: filter out telnet sequences! write_to_output(t->snoop_by, "%% %*s%%%%", result, t->output); + #endif /* The common case: all saved output was handed off to the kernel buffer. */ if (result >= t->bufptr) { *************** *** 1454,1459 **** --- 1529,1537 ---- t->bufptr = 0; *(t->output) = '\0'; + #if 0 + // TODO: I do not know how to handle this case yet! + /* * If the overflow message or prompt were partially written, try to save * them. There will be enough space for them if this is true. 'result' *************** *** 1466,1471 **** --- 1544,1550 ---- t->bufptr -= savetextlen; t->bufspace += savetextlen; } + #endif } else { /* Not all data in buffer sent. result < output buffersize. */ *************** *** 1587,1596 **** * >=0 If all is well and good. * -1 If an error was encountered, so that the player should be cut off. */ ! int write_to_descriptor(socket_t desc, const char *txt) { ssize_t bytes_written; ! size_t total = strlen(txt), write_total = 0; while (total > 0) { bytes_written = perform_socket_write(desc, txt, total); --- 1666,1675 ---- * >=0 If all is well and good. * -1 If an error was encountered, so that the player should be cut off. */ ! int write_to_descriptor(socket_t desc, const char *txt, const size_t length) { ssize_t bytes_written; ! size_t total = length, write_total = 0; while (total > 0) { bytes_written = perform_socket_write(desc, txt, total); *************** *** 1726,1733 **** *(read_point + bytes_read) = '\0'; /* terminate the string */ /* search for a newline in the data we just read */ ! for (ptr = read_point; *ptr && !nl_pos; ptr++) if (ISNEWL(*ptr)) nl_pos = ptr; --- 1805,1816 ---- *(read_point + bytes_read) = '\0'; /* terminate the string */ + /* process telnet handling */ + telnet_handle_input(t, read_point, bytes_read); + /* search for a newline in the data we just read */ ! /* for (ptr = read_point; *ptr && !nl_pos; ptr++) */ ! for (ptr = read_point; (ptr - read_point) < bytes_read && !nl_pos; ptr++) if (ISNEWL(*ptr)) nl_pos = ptr; *************** *** 1761,1795 **** read_point = t->inbuf; while (nl_pos != NULL) { ! write_point = tmp; ! space_left = MAX_INPUT_LENGTH - 1; ! /* The '> 1' reserves room for a '$ => $$' expansion. */ ! for (ptr = read_point; (space_left > 1) && (ptr < nl_pos); ptr++) { ! if (*ptr == '\b' || *ptr == 127) { /* handle backspacing or delete key */ ! if (write_point > tmp) { ! if (*(--write_point) == '$') { ! write_point--; ! space_left += 2; ! } else ! space_left++; ! } ! } else if (isascii(*ptr) && isprint(*ptr)) { ! if ((*(write_point++) = *ptr) == '$') { /* copy one character */ ! *(write_point++) = '$'; /* if it's a $, double it */ ! space_left -= 2; ! } else ! space_left--; ! } ! } ! *write_point = '\0'; if ((space_left <= 0) && (ptr < nl_pos)) { char buffer[MAX_INPUT_LENGTH + 64]; snprintf(buffer, sizeof(buffer), "Line too long. Truncated to:\r\n%s\r\n", tmp); ! if (write_to_descriptor(t->descriptor, buffer) < 0) return (-1); } if (t->snoop_by) --- 1844,1878 ---- read_point = t->inbuf; while (nl_pos != NULL) { ! write_point = tmp; ! space_left = MAX_INPUT_LENGTH - 1; ! /* The '> 1' reserves room for a '$ => $$' expansion. */ ! for (ptr = read_point; (space_left > 1) && (ptr < nl_pos); ptr++) { ! if (*ptr == '\b' || *ptr == 127) { /* handle backspacing or delete key */ ! if (write_point > tmp) { ! if (*(--write_point) == '$') { ! write_point--; ! space_left += 2; ! } else ! space_left++; ! } ! } else if (isascii(*ptr) && isprint(*ptr)) { ! if ((*(write_point++) = *ptr) == '$') { /* copy one character */ ! *(write_point++) = '$'; /* if it's a $, double it */ ! space_left -= 2; ! } else ! space_left--; ! } ! } ! *write_point = '\0'; if ((space_left <= 0) && (ptr < nl_pos)) { char buffer[MAX_INPUT_LENGTH + 64]; snprintf(buffer, sizeof(buffer), "Line too long. Truncated to:\r\n%s\r\n", tmp); ! if (write_to_descriptor(t->descriptor, buffer, strlen(buffer)) < 0) return (-1); } if (t->snoop_by) *************** *** 2518,2520 **** --- 2601,3218 ---- } #endif /* CIRCLE_WINDOWS */ + + + /* **************************************************************** + * Routines for telnet protocol handling * + **************************************************************** */ + + static unsigned char *process_will(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep); + static unsigned char *process_wont(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep); + static unsigned char *process_do(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep); + static unsigned char *process_dont(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep); + static unsigned char *process_sb(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep); + + static unsigned char *process_naws(struct descriptor_data *d, + unsigned char *ip); + static unsigned char *process_ttype(struct descriptor_data *d, + unsigned char *ip); + static unsigned char *process_linemode(struct descriptor_data *d, + unsigned char *ip); + + #ifdef DEBUG + static void telnet_log_input(const unsigned char *buf, const size_t length); + static void telnet_log_output(const unsigned char *buf, const size_t length); + #endif + + static int send_short_cmd(struct descriptor_data *d, int cmd) + { + unsigned char data[] = { + (unsigned char) IAC, + (unsigned char) cmd + }; + + write_to_output_2(d, data, sizeof(data)); + + return TRUE; + } + + static int send_cmd(struct descriptor_data *d, int cmd, int opt) + { + unsigned char data[] = { + (unsigned char) IAC, + (unsigned char) cmd, + (unsigned char) opt, + }; + + write_to_output_2(d, data, sizeof(data)); + + return TRUE; + } + + static int send_will(struct descriptor_data *d, int opt, int r) + { + if (!send_cmd(d, WILL, opt)) + return FALSE; + + if (!r) + d->telnet->tnt_sentWILL[opt] = TRUE; + + return TRUE; + } + + static int send_wont(struct descriptor_data *d, int opt, int r) + { + if (!send_cmd(d, WONT, opt)) + return FALSE; + + if (!r) + d->telnet->tnt_sentWONT[opt] = TRUE; + + return TRUE; + } + + static int send_do(struct descriptor_data *d, int opt, int r) + { + if (!send_cmd(d, DO, opt)) + return FALSE; + + if (!r) + d->telnet->tnt_sentDO[opt] = TRUE; + + return TRUE; + } + + static int send_dont(struct descriptor_data *d, int opt, int r) + { + if (!send_cmd(d, DONT, opt)) + return FALSE; + + if (!r) + d->telnet->tnt_sentDONT[opt] = TRUE; + + return TRUE; + } + + static int telnet_new_connection(struct descriptor_data *d) + { + CREATE(d->telnet, struct telnet_data, 1); + + /* start option negotioation */ + send_wont(d, TELOPT_BINARY, FALSE); + send_dont(d, TELOPT_BINARY, FALSE); + send_do (d, TELOPT_TTYPE , FALSE); + send_do (d, TELOPT_NAWS , FALSE); + send_wont(d, TELOPT_EOR , FALSE); + send_dont(d, TELOPT_EOR , FALSE); + + /* turn on client side echo */ + send_wont(d, TELOPT_ECHO , FALSE); + + // TODO: start line-mode negotiation + + return TRUE; + } + + static void telnet_handle_input(struct descriptor_data *d, char *buf, + const size_t length) + { + unsigned char *ip = (unsigned char *) buf; /* input pointer */ + unsigned char *op = (unsigned char *) buf; /* output pointer */ + unsigned char *ep = (unsigned char *) buf + length; /* end pointer */ + + #ifdef DEBUG + if (log_telnet_input) + telnet_log_input(ip, length); + #endif + + while (ip < ep) + { + if (*ip == IAC) + { + ip++; + + switch (*ip++) + { + case IAC: + if (d->telnet->tnt_flags & TELNET_RECV_BINARY) + *op++ = IAC; + break; + + case WILL: + ip = process_will(d, ip, ep); + break; + + case WONT: + ip = process_wont(d, ip, ep); + break; + + case DO: + ip = process_do(d, ip, ep); + break; + + case DONT: + ip = process_dont(d, ip, ep); + break; + + case SB: + ip = process_sb(d, ip, ep); + break; + + case NOP: + // no operation + break; + + case AYT: + // are you there + write_to_output(d, "CircleMUD 3.1"); + break; + + case GA: + // ignore this + break; + + case IP: // interrupt process (TODO: close connection?) + case EL: // erase line + case EC: // erase character + case AO: // abort output + break; + + default: + // TODO: what to do here? + break; + } + } + else + *op++ = *ip++; + } + } + + static void telnet_handle_output(struct descriptor_data *d, char *dest, + const char *buf, const size_t length) + { + const unsigned char *ip = (const unsigned char *) buf; /* input pointer */ + unsigned char *op = (unsigned char *) dest; /* output pointer */ + const unsigned char *ep = ip + length; /* end pointer */ + + if (!buf || !*buf) + return; + + while (ip < ep) + { + if (*ip == IAC) + { + /* double the IAC for binary connections */ + if (d->telnet->tnt_flags & TELNET_SEND_BINARY) + *op++ = IAC; + } + + *op++ = *ip++; + } + + #ifdef DEBUG + if (log_telnet_input) + telnet_log_output((unsigned char *) dest, op - (unsigned char *) dest); + #endif + } + + static unsigned char *process_will(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep) + { + static unsigned char ttype_data[] = { + (unsigned char) IAC, + (unsigned char) SB, + (unsigned char) TELOPT_TTYPE, + (unsigned char) TELQUAL_SEND, + (unsigned char) IAC, + (unsigned char) SE + }; + + int opt = *ip++; + + switch (opt) + { + case TELOPT_BINARY: + d->telnet->tnt_flags |= TELNET_RECV_BINARY; + if (!d->telnet->tnt_sentDO[opt]) + send_do(d, TELOPT_BINARY, TRUE); + break; + + case TELOPT_TTYPE: + write_to_output(d, ttype_data, sizeof(ttype_data)); + break; + + case TELOPT_EOR: + if (!d->telnet->tnt_sentDO[opt]) + send_dont(d, TELOPT_EOR, TRUE); // no, do NOT send EOR + break; + + default: + // TODO: log this? + if (!d->telnet->tnt_sentDO[opt] && !d->telnet->tnt_sentDONT[opt]) + send_dont(d, opt, TRUE); + break; + } + + if (d->telnet->tnt_sentDO[opt] || d->telnet->tnt_sentDONT[opt]) + { + d->telnet->tnt_sentDO [opt] = 0; + d->telnet->tnt_sentDONT[opt] = 0; + } + + return ip; + } + + static unsigned char *process_wont(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep) + { + int opt = *ip++; + + switch (opt) + { + case TELOPT_BINARY: + d->telnet->tnt_flags &= ~TELNET_RECV_BINARY; + if (!d->telnet->tnt_sentDO[opt]) + send_dont(d, TELOPT_BINARY, TRUE); + break; + + default: + // TODO: log this? + if (!d->telnet->tnt_sentDO[opt] && !d->telnet->tnt_sentDONT[opt]) + send_dont(d, opt, TRUE); + break; + } + + if (d->telnet->tnt_sentDO[opt] || d->telnet->tnt_sentDONT[opt]) + { + d->telnet->tnt_sentDO [opt] = 0; + d->telnet->tnt_sentDONT[opt] = 0; + } + + return ip; + } + + static unsigned char *process_do(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep) + { + int opt = *ip++; + + switch (opt) + { + case TELOPT_BINARY: + d->telnet->tnt_flags |= TELNET_SEND_BINARY; + if (!d->telnet->tnt_sentWILL[opt]) + send_will(d, TELOPT_BINARY, TRUE); + break; + + case TELOPT_EOR: + d->telnet->tnt_flags |= TELNET_SEND_EOR; + if (!d->telnet->tnt_sentWILL[opt]) + send_will(d, TELOPT_EOR, TRUE); + break; + + case TELOPT_SGA: + d->telnet->tnt_flags |= TELNET_SUPGA; + if (!d->telnet->tnt_sentWILL[opt] && !d->telnet->tnt_sentWONT[opt]) + send_will(d, TELOPT_SGA, TRUE); + break; + + default: + // TODO: log this? + if (!d->telnet->tnt_sentWILL[opt] && !d->telnet->tnt_sentWONT[opt]) + send_wont(d, opt, TRUE); + break; + } + + if (d->telnet->tnt_sentWILL[opt] || d->telnet->tnt_sentWONT[opt]) + { + d->telnet->tnt_sentWILL[opt] = 0; + d->telnet->tnt_sentWONT[opt] = 0; + } + + return ip; + } + + static unsigned char *process_dont(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep) + { + int opt = *ip++; + + switch (opt) + { + case TELOPT_BINARY: + d->telnet->tnt_flags &= ~TELNET_SEND_BINARY; + if (!d->telnet->tnt_sentWILL[opt]) + send_wont(d, TELOPT_BINARY, TRUE); + break; + + case TELOPT_EOR: + d->telnet->tnt_flags &= ~TELNET_SEND_EOR; + if (!d->telnet->tnt_sentWILL[opt]) + send_wont(d, TELOPT_EOR, TRUE); + break; + + case TELOPT_SGA: + d->telnet->tnt_flags &= ~TELNET_SUPGA; + if (!d->telnet->tnt_sentWILL[opt] && !d->telnet->tnt_sentWONT[opt]) + send_wont(d, TELOPT_SGA, TRUE); + break; + + default: + // TODO: log this? + if (!d->telnet->tnt_sentWILL[opt] && !d->telnet->tnt_sentWONT[opt]) + send_wont(d, opt, TRUE); + break; + } + + if (d->telnet->tnt_sentWILL[opt] || d->telnet->tnt_sentWONT[opt]) + { + d->telnet->tnt_sentWILL[opt] = 0; + d->telnet->tnt_sentWONT[opt] = 0; + } + + return ip; + } + + static unsigned char *process_sb(struct descriptor_data *d, + unsigned char *ip, unsigned char *ep) + { + int opt = *ip++; + + switch (opt) + { + case TELOPT_NAWS: + /* RFC 1073 */ + ip = process_naws(d, ip); + break; + + case TELOPT_TTYPE: + /* RFC 1091 */ + ip = process_ttype(d, ip); + break; + + case TELOPT_LINEMODE: + /* RFC 1184 */ + ip = process_linemode(d, ip); + break; + + default: + if (opt >= TELOPT_FIRST && opt <= TELOPT_LAST) + { + // TODO: log client name + log("SYSERR: Unknown telnet subnegotiation: %s", + TELOPT(opt)); + } + else + { + // TODO: log client name + log("SYSERR: Unknown telnet subnegotiation: %d", + opt); + } + + /* skip until the end of the negotiation */ + while (*ip != IAC || *(ip + 1) != SE) + ip++; + + break; + } + + if (*ip != IAC || *(ip + 1) != SE) + { + // TODO: log client name + log("SYSERR: Telnet subnegotiation not ended correctly"); + } + + return ip; + } + + static int get_naws_number(unsigned char **ipp) + { + int value = 0; + unsigned char *ip = *ipp; + + if (*ip == IAC) + { + value = IAC; + ip += 2; + } + else + { + value = *ip; + ip++; + } + + *ipp = ip; + + return value; + } + + static unsigned char *process_naws(struct descriptor_data *d, + unsigned char *ip) + { + int w = 0; + int h = 0; + + /* get the width, high byte */ + w += get_naws_number(&ip) << 8; + + /* get the width, low byte */ + w += get_naws_number(&ip); + + /* get the height, high byte */ + h += get_naws_number(&ip) << 8; + + /* get the height, low byte */ + h += get_naws_number(&ip); + + // TODO: use the terminal dimensions + + return ip; + } + + static unsigned char *process_ttype(struct descriptor_data *d, + unsigned char *ip) + { + char ttype[41]; // RFC 1091 states the maximum length is 40 characters + char *p = ttype; + int len = 0; + + // skip TELQUAL_IS + ip++; + + for (;;) + { + if (*ip == IAC && *(ip + 1) == SE) + break; + if (len++ >= 40) + break; + + *p++ = *ip++; + } + + *p = '\0'; // terminate string + + // TODO: do something with the terminal type + + return ip; + } + + static unsigned char *process_linemode(struct descriptor_data *d, + unsigned char *ip) + { + // TODO: implement this! + + /* for now, just skip ahead */ + while (*ip != IAC || *(ip + 1) != SE) + ip++; + + return ip; + } + + + + #ifdef DEBUG + static void telnet_log_io(const char *header, const unsigned char *buf, + const size_t length) + { + const unsigned char *inp = buf; + const unsigned char *endp = buf + length; + char output[MAX_STRING_LENGTH]; + + while (inp < endp) + { + if (*inp++ == IAC) + { + *output = '\0'; + + if (*inp < TELCMD_FIRST) + sprintf(output, "UNKNOWN COMMAND: %d", (int) *inp); + else + { + strcat(output, TELCMD(*inp)); + strcat(output, " "); + + switch (*inp++) + { + case IAC: + break; + + case DO: + case DONT: + case WILL: + case WONT: + //log("KALLE: *inp = %d", (int) *inp); + strcat(output, TELOPT(*inp)); + inp++; + break; + + case SB: + strcat(output, TELOPT(*inp)); + + switch (*inp++) + { + case TELOPT_NAWS: + sprintf(output + strlen(output), " %d %d %d %d", + *(inp + 0), *(inp + 1), + *(inp + 2), *(inp + 3)); + inp += 4; + break; + + case TELOPT_TTYPE: + if (*inp == TELQUAL_SEND) + { + strcat(output, " SEND"); + inp++; + } + else if (*inp == TELQUAL_IS) + { + strcat(output, " IS "); + inp++; + + while (*inp != IAC) + { + sprintf(output + strlen(output), "%c", *inp); + inp++; + } + } + break; + + default: + // find the terminating IAC SE + while (*inp != IAC || *(inp + 1) != SE) + inp++; + strcat(output, " [UNKNOWN]"); + break; + } + + sprintf(output + strlen(output), " %s %s", + TELCMD(*inp), TELCMD(*(inp + 1))); + inp += 2; + break; + + default: + break; + } + } + + if (*output) + log("(TELNET) %s IAC %s", header, output); + } + } + } + + static void telnet_log_input(const unsigned char *buf, const size_t length) + { + telnet_log_io("RECV", buf, length); + } + + static void telnet_log_output(const unsigned char *buf, const size_t length) + { + telnet_log_io("SEND", buf, length); + } + #endif diff -c orig-src/comm.h src/comm.h *** orig-src/comm.h Sat Jun 22 01:24:28 2002 --- src/comm.h Sat Dec 2 09:18:06 2006 *************** *** 31,37 **** /* I/O functions */ void write_to_q(const char *txt, struct txt_q *queue, int aliased); ! int write_to_descriptor(socket_t desc, const char *txt); size_t write_to_output(struct descriptor_data *d, const char *txt, ...) __attribute__ ((format (printf, 2, 3))); size_t vwrite_to_output(struct descriptor_data *d, const char *format, va_list args); void string_add(struct descriptor_data *d, char *str); --- 31,37 ---- /* I/O functions */ void write_to_q(const char *txt, struct txt_q *queue, int aliased); ! int write_to_descriptor(socket_t desc, const char *txt, const size_t length); size_t write_to_output(struct descriptor_data *d, const char *txt, ...) __attribute__ ((format (printf, 2, 3))); size_t vwrite_to_output(struct descriptor_data *d, const char *format, va_list args); void string_add(struct descriptor_data *d, char *str); Common subdirectories: orig-src/doc and src/doc diff -c orig-src/structs.h src/structs.h *** orig-src/structs.h Mon Nov 18 22:23:38 2002 --- src/structs.h Mon Nov 27 19:35:42 2006 *************** *** 977,982 **** --- 977,986 ---- }; + /* the telnet data structure is declared in comm.c */ + struct telnet_data; + + struct descriptor_data { socket_t descriptor; /* file descriptor for socket */ char host[HOST_LENGTH+1]; /* hostname */ *************** *** 1008,1013 **** --- 1012,1018 ---- struct descriptor_data *snooping; /* Who is this char snooping */ struct descriptor_data *snoop_by; /* And who is snooping this char */ struct descriptor_data *next; /* link to next descriptor */ + struct telnet_data *telnet; /* for telnet protocol handling (RFC854, ...) */ }; Common subdirectories: orig-src/util and src/util