/**************************************************************************
* 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"
bool write_to_descriptor args ((DESCRIPTOR_DATA * d, char *txt, int length));
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' };
#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->desc_flags, DESC_TELOPT_EOR))
SET_BIT (ch->comm, COMM_TELNET_EOR);
else
REMOVE_BIT (ch->comm, COMM_TELNET_EOR);
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)
{
/* 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, strlen (naws_do));
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->desc_flags, DESC_TELOPT_EOR);
else
REMOVE_BIT (d->desc_flags, DESC_TELOPT_EOR);
return;
}
void telopt_echo (DESCRIPTOR_DATA * d, bool state)
{
if (state)
SET_BIT (d->desc_flags, DESC_TELOPT_ECHO);
else
REMOVE_BIT (d->desc_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->desc_flags, DESC_TELOPT_NAWS))
return;
write_to_descriptor (d, naws_do, strlen (naws_do));
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->desc_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 (1, x, 150);
d->scr_height = URANGE (1, y, 80);
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), 4);
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);
#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;
}