/************************************************************************** * 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-2003 by Ryan Jennings * * http://1stmud.dlmud.com/ <r-jenn@shaw.ca> * ***************************************************************************/ #define TELOPTS #define TELCMDS #include "merc.h" #include "telnet.h" const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, NUL }; const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, NUL }; const char echo_dont[] = { IAC, DONT, TELOPT_ECHO, NUL }; const char echo_do[] = { IAC, DO, TELOPT_ECHO, NUL }; char eor_do[] = { IAC, DO, TELOPT_EOR, NUL }; char eor_will[] = { IAC, WILL, TELOPT_EOR, NUL }; char eor_dont[] = { IAC, DONT, TELOPT_EOR, NUL }; char eor_wont[] = { IAC, WONT, TELOPT_EOR, NUL }; char iac_logout[] = { IAC, DO, TELOPT_LOGOUT, NUL }; /* TTYSPEED .. used only to find out if the client is telopt capable */ char tspd_will[] = { IAC, WILL, TELOPT_TSPEED, NUL }; char tspd_do[] = { IAC, DO, TELOPT_TSPEED, NUL }; char tspd_wont[] = { IAC, WONT, TELOPT_TSPEED, NUL }; char tspd_dont[] = { IAC, DONT, TELOPT_TSPEED, NUL }; char naws_will[] = { IAC, WILL, TELOPT_NAWS, NUL }; char naws_dont[] = { IAC, DONT, TELOPT_NAWS, NUL }; char naws_do[] = { IAC, DO, TELOPT_NAWS, NUL }; char naws_wont[] = { IAC, WONT, TELOPT_NAWS, NUL }; char naws_sb[] = { IAC, SB, TELOPT_NAWS, NUL }; char iac_ip[] = { IAC, IP, NUL }; char iac_susp[] = { IAC, SUSP, NUL }; char iac_brk[] = { IAC, BREAK, NUL }; char iac_se[] = { IAC, SE, NUL }; const char go_ahead_str[] = { IAC, GA, NUL }; const char will_suppress_ga_str[] = { IAC, WILL, TELOPT_SGA, NUL }; const char wont_suppress_ga_str[] = { IAC, WONT, TELOPT_SGA, NUL }; #if !defined(NO_MCCP) char compress1_will[] = { IAC, WILL, TELOPT_COMPRESS, NUL }; char compress1_do[] = { IAC, DO, TELOPT_COMPRESS, NUL }; char compress1_dont[] = { IAC, DONT, TELOPT_COMPRESS, NUL }; char compress1_wont[] = { IAC, WONT, TELOPT_COMPRESS, NUL }; char compress2_will[] = { IAC, WILL, TELOPT_COMPRESS2, NUL }; char compress2_do[] = { IAC, DO, TELOPT_COMPRESS2, NUL }; char compress2_dont[] = { IAC, DONT, TELOPT_COMPRESS2, NUL }; char compress2_wont[] = { IAC, WONT, TELOPT_COMPRESS2, NUL }; #endif char msp_will[] = { IAC, WILL, TELOPT_MSP, NUL }; char msp_do[] = { IAC, DO, TELOPT_MSP, NUL }; char msp_dont[] = { IAC, DONT, TELOPT_MSP, NUL }; char msp_wont[] = { IAC, WONT, TELOPT_MSP, NUL }; char mxp_will[] = { IAC, WILL, TELOPT_MXP, NUL }; char mxp_do[] = { IAC, DO, TELOPT_MXP, NUL }; char mxp_dont[] = { IAC, DONT, TELOPT_MXP, NUL }; char mxp_wont[] = { IAC, WONT, TELOPT_MXP, NUL }; char s_mxp_supports[] = { "" MXPMODE(1) "<SUPPORTS" }; char s_mxp_version[] = { "" MXPMODE(1) "<VERSION" }; // Init string of Pueblo (pueblo.mozdev.org) // followed by the version char pueblo_str[] = { "PUEBLOCLIENT " }; char ptype_3klient[] = { "3klient " }; char imp_str[] = { "v1." }; char ttype_do[] = { IAC, DO, TELOPT_TTYPE, NUL }; char ttype_dont[] = { IAC, DONT, TELOPT_TTYPE, NUL }; char ttype_sb[] = { IAC, SB, TELOPT_TTYPE, NUL }; char ttype_send[] = { IAC, SB, TELOPT_TTYPE, SEND, IAC, SE, NUL }; char ttype_will[] = { IAC, WILL, TELOPT_TTYPE, NUL }; char ttype_wont[] = { IAC, WONT, TELOPT_TTYPE, NUL }; char binary_do[] = { IAC, DO, TELOPT_BINARY, NUL }; char binary_dont[] = { IAC, DONT, TELOPT_BINARY, NUL }; char binary_will[] = { IAC, WILL, TELOPT_BINARY, NUL }; char binary_wont[] = { IAC, WONT, TELOPT_BINARY, NUL }; PROTOTYPE(void init_mxp, (DESCRIPTOR_DATA *)); PROTOTYPE(void mxp_support, (DESCRIPTOR_DATA *, int, unsigned char *)); PROTOTYPE(void mxp_version, (DESCRIPTOR_DATA *, int, unsigned char *)); #define MTELOPT(string, command, len) \ if (!memcmp(&buf[i],(string),strlen((string)))) \ { \ unsigned char *p = &buf[i]; \ telopt_debug(d,p,strlen((string))+(len)+telopt_lskip,FALSE);\ i += strlen((string)) - 1; \ (command); \ i += len; \ i += telopt_lskip; \ telopt_lskip = 0; \ idx = i; \ continue; \ } #define MSTRING(string, command, len) \ if (!memcmp(&buf[i],(string),strlen((string)))) \ { \ (command); \ i += telopt_lskip; \ telopt_lskip = 0; \ idx = i; \ continue; \ } /* 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) { d_write(d, tspd_will, 0); return; } void telopt_init(DESCRIPTOR_DATA * d) { d_write(d, eor_will, 0); d_write(d, naws_do, 0); #if !defined(NO_MCCP) d_write(d, compress2_will, 0); d_write(d, compress1_will, 0); #endif d_write(d, msp_will, 0); d_write(d, mxp_will, 0); /* terminal type */ d_write(d, ttype_do, 0); d_write(d, binary_will, 0); return; } void telopt_debug(DESCRIPTOR_DATA * d, unsigned char *string, int length, bool send) { int i, j; static char buf[MSL]; /* static so gdb hardware watchpoint works */ unsigned char c; bool sb = FALSE; if (!d) return; sprintf(buf, "Telopt: [%d][%s][ ", d->descriptor, (send) ? "send" : "recv"); if (string[0] != IAC) return; for (i = 0; i < length; i++) { c = string[i]; if (TELCMD_OK(c)) { sprintf(buf + strlen(buf), "%s ", TELCMD(c)); if (c == SB) sb = TRUE; if (c == SE) sb = FALSE; continue; } else if (TELOPT_OK(c)) { sprintf(buf + strlen(buf), "%s ", TELOPT(c)); if (sb && c == TELOPT_NAWS) { for (j = 0; j < 4; j++) { i++; c = string[i]; sprintf(buf + strlen(buf), "%u ", c); } } if (sb && c == TELOPT_TTYPE) { i++; c = string[i]; if (c == SEND) { strcat(buf, "SEND "); } else if (c == IS) { strcat(buf, "IS "); do { i++; c = string[i]; sprintf(buf + strlen(buf), "%c", c); } while (c != IAC); strcat(buf, " "); } else { sprintf(buf + strlen(buf), "%u ", c); } } continue; } else { sprintf(buf + strlen(buf), "%u ", c); } } sprintf(buf + strlen(buf), "]"); wiznet(buf, NULL, NULL, WIZ_TELNET, 0, MAX_LEVEL - 2); return; } void telopt_ignore(void) { return; } /* telopt_send is just a wrapper for d_write, 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 length to the d_write! */ void telopt_send(DESCRIPTOR_DATA * d, char *string, int len) { d_write(d, string, len); 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_msp(DESCRIPTOR_DATA * d, bool state) { if (state) SET_BIT(d->d_flags, DESC_MSP); else REMOVE_BIT(d->d_flags, DESC_MSP); return; } void telopt_mxp(DESCRIPTOR_DATA * d, bool state) { if (state) { SET_BIT(d->d_flags, DESC_MXP); init_mxp(d); } else { REMOVE_BIT(d->d_flags, DESC_MXP); } return; } #if !defined(NO_MCCP) void telopt_compress(DESCRIPTOR_DATA * d, bool state, int version) { if (d->out_compress) return; if (state) compressStart(d, version); else compressEnd(d, version); return; } #endif 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; } /* telopt_unknown handles all the negotiation commands that the mud server doesn't understand */ int telopt_unknown(DESCRIPTOR_DATA * d, unsigned char c, char t, bool quiet) { char buf[MSL]; char cmd[MIL]; char opt[MIL]; char rev; char len = 1; if (c == '\n' || c == '\r' || c == NUL) return 0; if (TELCMD_OK(c)) { sprintf(cmd, "%s", TELCMD(c)); if (c == IAC) len = 1; else if (c >= SB) len = 2; else len = 1; } 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 if (t == 90) sprintf(opt, "MSP"); else if (t == 91) sprintf(opt, "MXP"); else sprintf(opt, "[%u]", t); switch (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); d_write(d, buf, 0); sprintf(buf, "Unknown to client: IAC %s %s", cmd, opt); log_string(buf); } return len; } /* this function is called when clients sends IAC WILL NAWS */ void telopt_naws_do(DESCRIPTOR_DATA * d) { if (DESC_FLAGGED(d, DESC_TELOPT_NAWS)) return; d_write(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, unsigned char *inbuf) { int x = 0, y = 0, t1, t2; SET_BIT(d->d_flags, DESC_TELOPT_NAWS); t1 = inbuf[i + 1]; t2 = inbuf[i + 2]; x = t2 + (t1 * 16); t1 = inbuf[i + 3]; t2 = inbuf[i + 4]; y = t2 + (t1 * 16); d->scr_width = URANGE(10, x, 250); d->scr_height = URANGE(10, y, 250); return; } void telopt_ttype(DESCRIPTOR_DATA * d, int i, int len, unsigned char *inbuf) { int n; int skip = 0; d->ttype[0] = NUL; SET_BIT(d->d_flags, DESC_TELOPT_TTYPE); for (n = 0; n <= len; n++) { if (inbuf[n + i + 2] == IAC) { if (inbuf[n + i + 3] == SE) { telopt_lskip = skip; return; } } else { if (n < 59) { d->ttype[n] = inbuf[n + i + 2]; d->ttype[n + 1] = NUL; } skip++; } } return; } void telopt_binary(DESCRIPTOR_DATA * d, bool state) { if (state) SET_BIT(d->d_flags, DESC_TELOPT_BINARY); else REMOVE_BIT(d->d_flags, DESC_TELOPT_BINARY); return; } void pueblo_client(DESCRIPTOR_DATA * d, int i, unsigned char *inbuf) { char *buf = (char *) &inbuf[i]; char send[MSL]; sscanf(buf, "PUEBLOCLIENT %lf", &d->pueblo_vers); telopt_lskip = strlen(buf); SET_BIT(d->d_flags, DESC_PUEBLO); sprintf(send, "\n\rPueblo version %.2f detected...", d->pueblo_vers); d_write(d, send, 0); return; } void portal_3klient(DESCRIPTOR_DATA * d, int i, unsigned char *inbuf) { char *buf = (char *) &inbuf[i]; static char tbuf[MSL]; int n; SET_BIT(d->d_flags, DESC_TELOPT_TTYPE); SET_BIT(d->d_flags, DESC_PORTAL); sscanf(buf, "3klient %u~%s\r\n", &d->portal.keycode, tbuf); for (n = 0; tbuf != NUL; n++) if (isdigit(tbuf[n])) break; snprintf(d->portal.version, 20, "Portal %s", &tbuf[n]); telopt_lskip = strlen(buf); d_print(d, "\n\rPortal GT Detected...", 0); return; } void fire_client(DESCRIPTOR_DATA * d, int i, unsigned char *inbuf) { char *buf = (char *) &inbuf[i]; int vers; SET_BIT(d->d_flags, DESC_IMP); sscanf(buf, "v1.%2d", &vers); telopt_lskip = strlen(buf); d->imp_vers = (double) ((vers * .01) + 1); d_printf(d, "\n\rFire Client version v%.2f detected...", d->imp_vers); 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, unsigned char *buf) { int i, idx; int iStart = strlen(d->inbuf); if (len <= 0) return; for (i = 0; i <= len; i++) { if (buf[i] == 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); 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); /* 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); /* Telnet Echo Negotiation */ MTELOPT(echo_dont, telopt_echo(d, FALSE), 0); MTELOPT(echo_do, telopt_echo(d, TRUE), 0); #if !defined(NO_MCCP) MTELOPT(compress2_will, telopt_compress(d, TRUE, 2), 0); MTELOPT(compress2_do, telopt_compress(d, TRUE, 2), 0); MTELOPT(compress2_wont, telopt_compress(d, FALSE, 2), 0); MTELOPT(compress2_dont, telopt_compress(d, FALSE, 2), 0); MTELOPT(compress1_will, telopt_compress(d, TRUE, 1), 0); MTELOPT(compress1_do, telopt_compress(d, TRUE, 1), 0); MTELOPT(compress1_wont, telopt_compress(d, FALSE, 1), 0); MTELOPT(compress1_dont, telopt_compress(d, FALSE, 1), 0); #endif MTELOPT(msp_will, telopt_msp(d, TRUE), 0); MTELOPT(msp_do, telopt_msp(d, TRUE), 0); MTELOPT(msp_wont, telopt_msp(d, FALSE), 0); MTELOPT(msp_dont, telopt_msp(d, FALSE), 0); MTELOPT(mxp_will, telopt_mxp(d, TRUE), 0); MTELOPT(mxp_do, telopt_mxp(d, TRUE), 0); MTELOPT(mxp_wont, telopt_mxp(d, FALSE), 0); MTELOPT(mxp_dont, telopt_mxp(d, FALSE), 0); /* Terminal Type */ MTELOPT(ttype_will, telopt_send(d, ttype_send, 6), 0); MTELOPT(ttype_sb, telopt_ttype(d, i, len, buf), 1); MTELOPT(ttype_do, telopt_ignore(), 0); MTELOPT(ttype_wont, telopt_ignore(), 0); MTELOPT(ttype_dont, telopt_ignore(), 0); MTELOPT(binary_will, telopt_binary(d, TRUE), 1); MTELOPT(binary_do, telopt_binary(d, TRUE), 1); MTELOPT(binary_dont, telopt_binary(d, FALSE), 1); MTELOPT(binary_wont, telopt_binary(d, FALSE), 1); /* IP and SUSP - kill the descriptor */ MTELOPT(iac_ip, telopt_close(d), 0); MTELOPT(iac_susp, telopt_close(d), 0); MTELOPT(iac_logout, telopt_ignore(), 0); /* BRK - ignore */ MTELOPT(iac_brk, telopt_ignore(), 0); /* SE - end of sub negotiation */ MTELOPT(iac_se, telopt_ignore(), 0); if (buf[i + 1] == IAC) continue; /* No match for IAC sequence was found */ i += telopt_unknown(d, buf[i + 1], buf[i + 2], FALSE); } else { MSTRING(pueblo_str, pueblo_client(d, i, buf), 0); MSTRING(ptype_3klient, portal_3klient(d, i, buf), 0); MSTRING(imp_str, fire_client(d, i, buf), 0); MSTRING(s_mxp_version, mxp_version(d, i, buf), 0); MSTRING(s_mxp_supports, mxp_support(d, i + 4, buf), 0); switch (buf[i]) { case '~': // turn any ~ into {- d->inbuf[iStart] = ANSI_KEY; iStart++; d->inbuf[iStart] = '-'; break; default: d->inbuf[iStart] = buf[i]; break; } iStart++; } } return; } /* 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; }