mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@hom.net 
MUD++ development mailing list    mudpp@van.ml.org
------------------------------------------------------------------------------
shell.cc
*/

// Pseudo-terminals - Whheee!
// This code is not very obvious.
// It is concerned with handled pseudo-terminals (pty for short)
// To run terminal emulation across a standard network descriptor
// we open a master pty, fork a slave which opens the slave side of the pty,
// maps the pty onto stdin/sdtdout/stderr and then execs the shell process.
// Code for BSD is completely different from SYSV so please report problems.
// Recommended reading:
//    -Stevens' UNIX Network Programming
//    -Berkeley Software Distribution source code (telnetd and rlogind) 

#if 1  

#include <fcntl.h>
#include <termio.h>
#include "config.h"
#include "socket.h"

#if defined(SYSV) || defined(_SYSV)
#include <stropts.h>
#ifndef PTY_MASTER_DEV_SYSV
#define PTY_MASTER_DEV_SYSV "/dev/ptmx"
#endif
static struct termio tty;
extern "C" {
int grantpt( int );
int unlockpt( int );
char *ptsname( int );
}
#else /* BSD */
#include <sys/stat.h>

#ifndef WIN32
#include <sys/ioctl.h>
#endif

#endif

#if defined(WIN32)
#include <io.h>
#endif //WIN32

int route_io( int, int );
int open_pty_master( char * );
int open_pty_slave( int, const char * );

// Mini-telnetd
// This is not meant to be an attempt to implement telnetd
// Berkeley telnetd is about 7000 lines of code. A lot of it is
// conditional switches for various non-standard OS. I just want
// to implement enough to be functional. With time it will probably
// grow as reports come in from OS that need different stuff.

#define DEBUG 1

const int ST_DATA		= 0;
const int ST_IAC		= 1;
const int ST_SE			= 2;
const int ST_OPT		= 3;
const int ST_HOW		= 4;
const int ST_WILL		= 5;
const int ST_WONT		= 6;
const int ST_DO			= 7;
const int ST_DONT		= 8;
const int ST_CRLF		= 9;
const int ST_IP			= 10;

int route_io( int fd1, int fd2 )
{
	Descriptor fdnet( fd1 );
	Descriptor fdpty( fd2 );
	struct timeval sleep_time;
	fd_set r_set, w_set, o_set;
	int state = ST_DATA;
	char netbuf[1024];
	char netbuf2[1024];
	char ptybuf[1024];
	char ptybuf2[1024*2];
	int netbytes = 0;
	int ptybytes = 0;
	int maxdesc = fdnet;
	if( maxdesc < fdpty )
		maxdesc = fdpty;

	FD_ZERO( &r_set );
	FD_ZERO( &w_set );
	FD_ZERO( &o_set );
	for( ; ; )
	{
		sleep_time.tv_sec = 1;
		sleep_time.tv_usec = 0;

		FD_SET( (int)fdnet, &r_set );
		FD_SET( (int)fdnet, &w_set );
		FD_SET( (int)fdnet, &o_set );
		FD_SET( (int)fdpty, &r_set );
		FD_SET( (int)fdpty, &w_set );
		// First sleep until data to be read. Then poll the writeable
		// descriptors too. This is best for conserving CPU and gives
		// the best interactive performance from my tests. Of course
		// data only gets written on 'read' pulses but testing hasn't
		// shown this to be a problem.
		// Don't bother checking error codes here.
		select( maxdesc+1, &r_set, 0, 0, &sleep_time );

		// Now do the write poll which almost always returns ready. 
		sleep_time.tv_sec = 1;
		sleep_time.tv_usec = 0;
		while( select( maxdesc+1, 0, &w_set, &o_set, &sleep_time ) < 0 )
		{
			if( errno == EINTR )
				continue;
			perror( "route_io:select" );
			return -1;
		}

		if( FD_ISSET( (int)fdnet, &o_set ) )
		{
			printf( "Got OOB data.\n" );
			// add code to deal with it soon
		}

		if( FD_ISSET( (int)fdnet, &r_set ) && netbytes == 0 )
		{
			if( ( netbytes = fdnet.read( netbuf, sizeof( netbuf ) ) ) < 0 )
			{
#ifdef DEBUG
				perror( "route_io:read-from-fdnet" );
#endif
				return -1;
			}
			else if( netbytes == 0 )
				if( fdnet.eof() )
					break;
		}

		if( FD_ISSET( (int)fdpty, &w_set ) && netbytes > 0 )
		{
			int i = 0;
			int j = 0;
			int ch;

			// After about 80% I looked at Berkeley telnetd and was surprised
			// that it uses a similar state machine. Also the CRLF
			// stuff here is based on my understanding of Berkeley sources.
			while( i < netbytes )
			{
				ch = netbuf[i++] & 0377;
				switch( state )
				{
				default:
				case ST_CRLF:	// Skip '\n' if '\r\n'
					state = ST_DATA;
					if( ch == '\n' || ch == '\0' )
						continue;
					// Fall through to data
				case ST_DATA:
					if( ch == IAC )
					{
						state = ST_IAC;
						continue;
					}
					else if( ch == '\r' )
						state = ST_CRLF;
					netbuf2[j++] = ch;	
					continue;
				case ST_IAC:
					// Messy. Eat up IACs for now.
					if( ch == SB )
						state = ST_SE;
					else if( ch == IP )
					{
						// Kludgy! Will handle data-mark and urgent
						// data later, right now just kill the shell.
						exit(0);
					}
					else if( ch == IAC ) // double IAC can happen
					{
						netbuf2[j++] = ch;
						state = ST_DATA;
					}
					else state = ST_OPT;
					break;
				case ST_OPT:
				case ST_DO:
				case ST_DONT:
				case ST_WILL:
				case ST_WONT:
					// will/wont/do/dont - discarding for now
					state = ST_DATA;
					break;
				case ST_SE:
					if( ch == SE )
						state = ST_DATA;
					break;
				}
			}

			// If error assume shell exited
			if( fdpty.write( netbuf2, j ) < 0 )
			{
#ifdef DEBUG
				perror( "fdpty.write" );
#endif
				return -1;
			}
			netbytes = 0;
		}

		// Pty has data
		if( FD_ISSET( (int)fdpty, &r_set ) && ptybytes == 0 )
		{
			if( ( ptybytes = fdpty.read( ptybuf, sizeof( ptybuf ) ) ) < 0 )
			{
#ifdef DEBUG
				perror( "fdpty.read" );
#endif
				return -1;
			}
			else if( ptybytes == 0 )
				if( fdpty.eof() )
					break;
		}

		if( FD_ISSET( (int)fdnet, &w_set ) && ptybytes > 0 )
		{
			int i = 0;
			int j = 0;
			int ch;
			// This little snippet is from reading Berkeley sources
			// trying to understand the telnet protocol.
			// If we were supporting binary mode (RFC 856) we would need
			// more than what I have. IAC is a legal byte in binary
			// mode so if we get double IAC we just pass one thru.
			// ---
			// Concerning CRLF, since we are the server (UNIX side)
			// we only expect '\n' so translate for client. If we
			// see '\r' from server side, ignore since it was probably
			// part of some other app trying to do translation like us.
			// This is only my understanding, which is very limited.
			while( i < ptybytes )
			{
				ch = ptybuf[i++] & 0377;
				if( ch == IAC )
					ptybuf2[j++] = ch;
				else if( ch == '\n' )
				{
					ptybuf2[j++] = '\r';
					ptybuf2[j++] = ch; 
				}
				else if( ch != '\r' )
					ptybuf2[j++] = ch;
			}

			if( fdnet.write( ptybuf2, j ) < 0 )
			{
#ifdef DEBUG
				perror( "route_io:write-to-fdnet" );
#endif
				return -1;
			}
			ptybytes = 0;
		}
	}
	return 0;
}

#ifdef SYSV
// SYSV doesn't use the pty name
int open_pty_master( char * )
{
	int fd;
	if( ( fd = open( PTY_MASTER_DEV_SYSV, O_RDWR ) ) < 0 )
		return -1;
	return fd; 
}

int open_pty_slave( int master_fd, const char * )
{
	int fd;
	char *slavepty;

	if( grantpt( master_fd ) < 0 )
	{
		close( master_fd );
		return -1;
	}

	if( unlockpt( master_fd ) < 0 )
	{
		close( master_fd );
		return -1;
	}

	slavepty = ptsname( master_fd );
	if( !slavepty )
	{
		close( master_fd );
		return -1;
	}

	if( ( fd = open( slavepty, O_RDWR ) ) < 0 )
	{
		close( master_fd );
		return -1;
	}

	// Push streams modules
	// ptem = pseudo terminal emulation module
	// ldterm = terminal line discipline
	if( ioctl( fd, I_PUSH, "ptem" ) < 0 )
	{
		close( master_fd );
		return -1;
	}

	if( ioctl( fd, I_PUSH, "ldterm" ) < 0 )
	{
		close( master_fd );
		return -1;
	}

	return fd;
}
#else /* 4.3 BSD */

// BSD pty's are in form ptyXY ( ptyp0 to ptysf ) where
// X is p-s and Y is 0-f hex
// Increment through until we succeed in opening one.
// The slave side of a BSD ptyXY is ttyXY
int open_pty_master( char *pty )
{
	int master_fd;
	struct stat s;
	const char * Xchars = "pqrs";
	const char * Ychars = "0123456789abcdef";
	int i,j;

	strcpy( pty, "/dev/ptyXY" );
	for( i = 0; Xchars[i]; i++ )
	{
		pty[8] = Xchars[i];
		pty[9] = '0';

		// See if system has /dev entry for ptyX0, if not we are done 
		if( stat( pty, &s ) < 0 )
			break;

		for( j=0; j < 16; j++ )
		{
			pty[9] = Ychars[j];
			if( ( master_fd = open( pty, O_RDWR ) ) >= 0 )
				return master_fd; // grab it and run
		}
	}

	return -1;
}


int open_pty_slave( int master_fd, const char *pty )
{
	int slave_fd;
	char tty[12];

	// Change /dev/ptyXY to /dev/ttyXY (the slave side)
	strcpy( tty, pty );
	tty[5] = 't';
	if( ( slave_fd = open( tty, O_RDWR ) ) < 0 )
	{
		close( master_fd );
		return -1;
	}

	return slave_fd;
}
#endif
#endif   // remove this to try shell code