/************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Much time and thought has gone into this software and you are * * benefiting. We hope that you share your changes too. What goes * * around, comes around. * *************************************************************************** * ROM 2.4 is copyright 1993-1998 Russ Taylor * * ROM has been brought to you by the ROM consortium * * Russ Taylor (rtaylor@hypercube.org) * * Gabrielle Taylor (gtaylor@hypercube.org) * * Brian Moore (zump@rom.org) * * By using this code, you have agreed to follow the terms of the * * ROM license, in the file Rom24/doc/rom.license * *************************************************************************** * 1stMUD ROM Derivative (c) 2001-2002 by Ryan Jennings * * http://1stmud.dlmud.com/ <r-jenn@shaw.ca> * ***************************************************************************/ #define TELOPTS #define TELCMDS #include <sys/types.h> #include <stdio.h> #include <string.h> #include "merc.h" #include "telnet.h" char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, '\0' }; char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, '\0' }; char echo_dont[] = { IAC, DONT, TELOPT_ECHO, '\0' }; char echo_do[] = { IAC, DO, TELOPT_ECHO, '\0' }; /* Telnet End-Of-Record */ char eor_do[] = { IAC, DO, TELOPT_EOR, '\0' }; char eor_will[] = { IAC, WILL, TELOPT_EOR, '\0' }; char eor_dont[] = { IAC, DONT, TELOPT_EOR, '\0' }; char eor_wont[] = { IAC, WONT, TELOPT_EOR, '\0' }; /* Telnet window size */ char naws_will[] = { IAC, WILL, TELOPT_NAWS, '\0' }; char naws_dont[] = { IAC, DONT, TELOPT_NAWS, '\0' }; char naws_do[] = { IAC, DO, TELOPT_NAWS, '\0' }; char naws_wont[] = { IAC, WONT, TELOPT_NAWS, '\0' }; char naws_sb[] = { IAC, SB, TELOPT_NAWS, '\0' }; /* kill the socket after receiving either of those 2 strings */ char iac_ip[] = { IAC, IP, '\0' }; char iac_susp[] = { IAC, SUSP, '\0' }; char iac_brk[] = { IAC, BREAK, '\0' }; /* telnet subnegotiation end */ char iac_se[] = { IAC, SE, '\0' }; /* go ahead after prompt string */ const char go_ahead_str[] = { IAC, GA, '\0' }; /* TTYSPEED .. used only to find out if the client is telopt capable */ char tspd_will[] = { IAC, WILL, TELOPT_TSPEED, '\0' }; char tspd_do[] = { IAC, DO, TELOPT_TSPEED, '\0' }; char tspd_wont[] = { IAC, WONT, TELOPT_TSPEED, '\0' }; char tspd_dont[] = { IAC, DONT, TELOPT_TSPEED, '\0' }; #if !defined(NO_MCCP) /* mccp: compression negotiation strings */ char compress_will[] = { IAC, WILL, TELOPT_COMPRESS, '\0' }; char compress_do[] = { IAC, DO, TELOPT_COMPRESS, '\0' }; char compress_dont[] = { IAC, DONT, TELOPT_COMPRESS, '\0' }; #endif #define MTELOPT(string, command, len) \ if (!memcmp(&buf[i],(string),strlen((string)))) \ { \ i += strlen((string)) - 1; \ (command); \ i += len; \ idx = i; \ continue; \ } /* descriptor flags are used because some mud clients initiate the telnet negotiation before the player logs on, this way as long there is a socket open there is also flag strip that can indicate various results of the negotiation. */ void set_desc_flags(DESCRIPTOR_DATA * d) { CHAR_DATA *ch = CH(d); if (!ch) return; if (IS_SET(d->d_flags, DESC_TELOPT_EOR)) SET_BIT(ch->comm, COMM_TELNET_EOR); else REMOVE_BIT(ch->comm, COMM_TELNET_EOR); if (IS_SET(ch->comm, COMM_NOCOLOUR)) REMOVE_BIT(d->d_flags, DESC_COLOUR); return; } /* init_telnet initiates the telnet negotiation with the mud client, although most of the mud clients support it, there is still few dinosaurs that don't gmud is one of them. */ void init_telnet(DESCRIPTOR_DATA * d) { write_to_descriptor(d, tspd_will, 0); return; } void telopt_init(DESCRIPTOR_DATA * d) { /* telnet end-of-record */ write_to_descriptor(d, eor_will, 0); #if !defined(NO_MCCP) write_to_descriptor(d, compress_will, 0); #endif /* telnet window size negotiation */ write_to_descriptor(d, naws_do, 0); return; } void telopt_ignore(void) { return; } /* telopt_send is just a wrapper for write_to_descriptor, if you ever need to send any string to the client that contains '\0' then you will have to modify this so it reports the correct string lenght to the write_to_descriptor! */ void telopt_send(DESCRIPTOR_DATA * d, char *string) { write_to_descriptor(d, string, strlen(string)); return; } /* IAC IP or IAC SUSP means that the clients machine lost control connection and you can safely drop the link of that player */ void telopt_close(DESCRIPTOR_DATA * d) { wiznet("IAC IP/SUSP received, closing link to $N.", CH(d), NULL, WIZ_LINKS, 0, 0); close_socket(d); return; } /* TELOPT_EOR works with few clients exactly the same way as TELOPT_GA the only advantage of it is that it can be auto-negotiated so players won't have to turn it on or off */ void telopt_eor(DESCRIPTOR_DATA * d, bool state) { if (state) SET_BIT(d->d_flags, DESC_TELOPT_EOR); else REMOVE_BIT(d->d_flags, DESC_TELOPT_EOR); return; } void telopt_echo(DESCRIPTOR_DATA * d, bool state) { if (state) SET_BIT(d->d_flags, DESC_TELOPT_ECHO); else REMOVE_BIT(d->d_flags, DESC_TELOPT_ECHO); return; } #if !defined(NO_MCCP) void telopt_compress(DESCRIPTOR_DATA * d, bool state) { if (state) compressStart(d); else compressEnd(d); return; } #endif /* telopt_unknown handles all the negotiation commands that the mud server doesn't understand */ int telopt_unknown(DESCRIPTOR_DATA * d, unsigned char c, unsigned char t, bool quiet) { char buf[MAX_STRING_LENGTH]; char cmd[MAX_INPUT_LENGTH]; char opt[MAX_INPUT_LENGTH]; char rev; char len = 1; if (c == '\n' || c == '\r' || c == '\0') return 0; if (TELCMD_OK(c)) { sprintf(cmd, "%s", TELCMD(c)); if (c == IAC) len = 1; /* IAC IAC */ else if (c >= SB) len = 2; /* IAC DONT/DO/WONT/WILL/SB ?? */ else len = 1; /* IAC ?? */ } if (!quiet) { if (TELCMD_OK(c)) sprintf(cmd, "%s", TELCMD(c)); else sprintf(cmd, "[%u]", c); if (TELOPT_OK(t)) sprintf(opt, "%s", TELOPT(t)); else if (TELCMD_OK(t)) sprintf(opt, "%s", TELCMD(t)); else if (t == 85) sprintf(opt, "COMPRESS-1"); else if (t == 86) sprintf(opt, "COMPRESS-2"); else sprintf(opt, "[%u]", t); switch ((unsigned char) c) { case WILL: rev = WONT; break; case WONT: rev = DONT; break; case DONT: rev = WONT; break; case DO: rev = DONT; break; default: rev = c; break; } sprintf(buf, "%c%c%c", IAC, rev, t); write_to_descriptor(d, buf, 0); } return len; } /* this function is called when clients sends IAC WILL NAWS */ void telopt_naws_do(DESCRIPTOR_DATA * d) { return; /* IAC WILL NAWS second time around */ if (IS_SET(d->d_flags, DESC_TELOPT_NAWS)) return; write_to_descriptor(d, naws_do, 0); return; } /* this function handles the actual NAWS sub negotiation and takes the 4 bytes of data between IAC SB NAWS and IAC SE and converts them to the clients screen dimensions, then saves them in d->scr_width and d->scr_height */ void telopt_naws(DESCRIPTOR_DATA * d, int i, char *inbuf) { unsigned int x = 0, y = 0, t1, t2; SET_BIT(d->d_flags, DESC_TELOPT_NAWS); t1 = (unsigned char) inbuf[i + 1]; t2 = (unsigned char) inbuf[i + 2]; x = t2 + (t1 * 16); t1 = (unsigned char) inbuf[i + 3]; t2 = (unsigned char) inbuf[i + 4]; y = t2 + (t1 * 16); d->scr_width = URANGE(10, x, 250); d->scr_height = URANGE(10, y, 250); return; } /* process_telnet loops thru the buffer passed to it from read_from_descriptor and takes care of any IAC sequences found, at the same time it appends everything else to the end of d->inbuf */ void process_telnet(DESCRIPTOR_DATA * d, int len, char *buf) { unsigned int i, idx; int iStart = strlen(d->inbuf); if (len <= 0) return; /* nothing to process */ for (i = 0; i <= (unsigned) len; i++) { if (buf[i] == (signed char) IAC) { /* Telnet Window Size Negotiation */ MTELOPT(naws_sb, telopt_naws(d, i, buf), 6); MTELOPT(naws_will, telopt_naws_do(d), 0); MTELOPT(naws_wont, telopt_ignore(), 0); /* Telnet End-Of-Record Negotiation */ MTELOPT(eor_will, telopt_eor(d, TRUE), 0); MTELOPT(eor_do, telopt_eor(d, TRUE), 0); MTELOPT(eor_wont, telopt_eor(d, FALSE), 0); MTELOPT(eor_dont, telopt_eor(d, FALSE), 0); MTELOPT(tspd_will, telopt_init(d), 0); MTELOPT(tspd_do, telopt_init(d), 0); MTELOPT(tspd_wont, telopt_init(d), 0); MTELOPT(tspd_dont, telopt_init(d), 0); #if !defined(NO_MCCP) MTELOPT(compress_will, telopt_compress(d, TRUE), 0); MTELOPT(compress_do, telopt_compress(d, TRUE), 0); MTELOPT(compress_dont, telopt_compress(d, FALSE), 0); #endif /* Telnet Echo Negotiation */ MTELOPT(echo_dont, telopt_echo(d, FALSE), 0); MTELOPT(echo_do, telopt_echo(d, TRUE), 0); /* IP and SUSP - kill the descriptor */ MTELOPT(iac_ip, telopt_close(d), 0); MTELOPT(iac_susp, telopt_close(d), 0); /* BRK - ignore */ MTELOPT(iac_brk, telopt_ignore(), 0); /* SE - end of sub negotiation */ MTELOPT(iac_se, telopt_ignore(), 0); if (buf[i + 1] == (signed char) IAC) continue; /* No match for IAC sequence was found */ i += telopt_unknown(d, buf[i + 1], buf[i + 2], FALSE); } else { d->inbuf[iStart] = buf[i]; iStart++; } } d->inbuf[iStart] = '\0'; return; }