/
umud/DOC/
umud/DOC/examples/
umud/DOC/internals/
umud/DOC/wizard/
umud/MISC/
umud/MISC/dbchk/
umud/RWHO/rwhod/
/*
	Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/

#ifndef	lint
static	char	RCSid[] = "$Header: /usr/users/mjr/hacks/umud/RCS/xmit.c,v 1.1 91/08/19 01:14:44 mjr Exp $";
#endif

/* configure all options BEFORE including system stuff. */
#include	"config.h"


#include	<stdio.h>

#ifdef	NOSYSTYPES_H
#include	<types.h>
#else
#include	<sys/types.h>
#endif

#include	<errno.h>
extern int	errno;

#include	<ctype.h>
#include	<varargs.h>
#include	<fcntl.h>
#include	<sys/time.h>
#include	<sys/file.h>
#include	<sys/socket.h>
#include	<netinet/in.h>
#include	<netdb.h>
#include	<signal.h>

#include	"mud.h"
#include	"xact.h"
#include	"sbuf.h"

#ifndef	FD_SET
#define NBBY    8
#define NFDBITS (sizeof(long) * NBBY)
#define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p)      bzero((char *)(p), sizeof(*(p)))
#endif

/*
#define	XMITBSD_DEBUG
*/

/* don't bother being too big - this is a TCP */
#define	XMBUFSIZ	1024

/* default timeout for I/O or connections */
#define	DEFAULT_TIMEOUT	2


/* mud database entry */
typedef	struct	mudent {
	int			flg;
	char			*name;
	char			*plyport;
	char			*host;
	char			*symhost;
	char			*rempw;
	char			*locpw;
	struct sockaddr_in	addr;
	int			timot;
	struct	mudent		*next;
} Mudent;


/* buffered connection */
typedef	struct	{
	int	fd;
	char	ibuf[XMBUFSIZ];
	char	*ibp;
	int	ibc;
	char	obuf[XMBUFSIZ];
	char	*obp;
	Mudent	*curmp;
} Cnxt;


static	Mudent	*getmudent();
static	Mudent	*mud_list;
static	Cnxt	xbuf;
static	Mudent	*lastmud = (Mudent *)0;

/* Look up the remote MUD and set the context up right */

set_remmud(nam)
char	*nam;
{
	Mudent	*mp;

	if((mp = getmudent(nam)) == (Mudent *)0)
		return(1);

	lastmud = xbuf.curmp = mp;
	return(0);
}



xc_open(mud)
char	*mud;
{
	Mudent	*mp;

	if((mp = getmudent(mud)) == (Mudent *)0) {
		logf("unknown mud ",mud,"\n",(char *)0);
		return(-1);
	}

	if((xbuf.fd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
		perror("socket");
		return(-1);
	}
	fcntl(xbuf.fd,F_SETFL,FNDELAY);

	/* this may EWOULDBLOCK, which is OK */
	if(connect(xbuf.fd,&(mp->addr),sizeof(struct sockaddr_in)) < 0) {
		fd_set	msk;
		sig_t	sigsaved;
		struct	timeval	timo;

#ifdef	EINPROGRESS
		if(errno != EWOULDBLOCK && errno != EINPROGRESS) {
#else
		if(errno != EWOULDBLOCK) {
#endif
			(void)shutdown(xbuf.fd,2);
			(void)close(xbuf.fd);
#ifdef	XMITBSD_DEBUG
			perror("connect");
#endif
			logf("connection to ",mud," failed ",(char *)-1,"\n",(char *)0);
			return(-1);
		}

		FD_ZERO(&msk);
		FD_SET(xbuf.fd,&msk);
		timo.tv_usec = 0;
		timo.tv_sec = mp->timot;

		if(select(xbuf.fd + 1,0,&msk,0,&timo) != 1) {
			(void)shutdown(xbuf.fd,2);
			(void)close(xbuf.fd);
#ifdef	XMITBSD_DEBUG
			perror("connect failed (timeout?)");
#endif
			logf("connection to ",mud," timeout ",(char *)-1,"\n",(char *)0);
			return(-1);
		}
		/* Probe the fd to see if the connect() worked */

		sigsaved = signal(SIGPIPE,SIG_IGN);
		if(write(xbuf.fd,"",0) < 0) {
			(void)signal(SIGPIPE,sigsaved);
			return(-1);
		}
		(void)signal(SIGPIPE,sigsaved);
	}

#ifdef	XMITBSD_DEBUG
	(void)printf("<<<connected>>>\n");
#endif
	logf("connection to ",mud,"\n",(char *)0);
	xbuf.ibp = xbuf.ibuf;
	xbuf.ibc = 0;
	xbuf.obp = xbuf.obuf;
	*xbuf.ibp = *xbuf.obp = '\0';
	lastmud = xbuf.curmp = mp;
	return(0);
}




xc_initfd(fd)
int	fd;
{
	xbuf.fd = fd;
	xbuf.ibp = xbuf.ibuf;
	xbuf.ibc = 0;
	xbuf.obp = xbuf.obuf;
	*xbuf.ibp = *xbuf.obp = '\0';
	fcntl(xbuf.fd,F_SETFL,FNDELAY);
	return(0);
}




/* Send out an OIF Level 2 greet string to the current mud */
xmit_greet()
{
	/* Firewall. This should NEVER happen. */
	if(xbuf.curmp == (Mudent *)0)
		return(-1);

	if(xc_write("UnterMUD ",mud_getname()," ",version," "
			,(xbuf.curmp)->locpw,"\n",(char *)0))
		return(-1);
	if(xc_flush())
		return(-1);

	return(0);

}




/* Return 0 if pwd is OK */
check_rempwd(pwd)
char	*pwd;
{
	/* NEVER happen. */
	if(xbuf.curmp == (Mudent *)0)
		return(1);

	return(strcmp(pwd,(xbuf.curmp)->rempw));
}




/* These grub data out of the LAST Mud contacted.  */
char *
xc_getips()
{
	extern	char	*inet_ntoa();

	if(lastmud == (Mudent *)0)
		return("");
	return( inet_ntoa(lastmud->addr.sin_addr) );
}




char *
xc_getmudname()
{
	if(lastmud == (Mudent *)0){
		return("");
	}
	return(lastmud->name);
}




char *
xc_gethostname()
{
	if(lastmud == (Mudent *)0)
		return("");
	return(lastmud->symhost);
}




char *
xc_getport()
{
	if(lastmud == (Mudent *)0)
		return("");
	return(lastmud->plyport);
}




static Mudent	*
getmudent(mud)
char	*mud;
{
	Mudent	*mp = mud_list;

	while(mp != (Mudent *)0) {
		if(!strcmp(mud,mp->name))
			return(mp);
		mp = mp->next;
	}
	return((Mudent *)0);
}




/*
define a MUD entry. One semi-sensible thing done here is to
pre-resolve the addresses and just stash those. this way
we shouldn't have to hit our resolver a whole lot once the
MUD is up, if at all. also accept "dot" notation.
*/
xmit_def_mudent(name,host,symhost,rempw,locpw,port,plyport,timot)
char	*name;
char	*host;
char	*symhost;
char	*rempw;
char	*locpw;
char	*port;
char	*plyport;
char	*timot;
{
	Mudent	*np;
#ifndef	NO_HUGE_RESOLVER_CODE
	struct	hostent	*hp;
#endif
	struct	sockaddr_in	tmpa;
	char	*p;

	p = host;
	while(*p != '\0' && (*p == '.' || isdigit(*p)))
		p++;

	if(*p != '\0') {
#ifndef	NO_HUGE_RESOLVER_CODE
		if((hp = gethostbyname(host)) == (struct hostent *)0) {
			logf("unknown host ",host,"\n",(char *)0);
			return(1);
		}
		(void)bcopy(hp->h_addr,(char *)&tmpa.sin_addr,hp->h_length);
#else
		logf("must use 'dot' notation to define host ",host,"\n",(char *)0);
		return(1);
#endif
	} else {
		unsigned long	f;

		if((f = inet_addr(host)) == -1L)
			return(1);
		(void)bcopy((char *)&f,(char *)&tmpa.sin_addr,sizeof(f));
	}

	if((np = (Mudent *)malloc(sizeof(Mudent))) == (Mudent *)0) {
		logf("cannot allocate remote mud map\n",(char *)0);
		return(1);
	}

	(void)bcopy((char *)&tmpa.sin_addr,&(np->addr.sin_addr),sizeof(tmpa.sin_addr));
	np->addr.sin_port = htons(atoi(port));
	np->addr.sin_family = AF_INET;

	if(timot != (char *)0)
		np->timot = atoi(timot);
	else
		np->timot = DEFAULT_TIMEOUT;


	np->name = (char *)malloc((unsigned)(strlen(name) + 1));
	np->rempw = (char *)malloc((unsigned)(strlen(rempw) + 1));
	np->locpw = (char *)malloc((unsigned)(strlen(locpw) + 1));
	np->plyport = (char *)malloc((unsigned)(strlen(plyport) + 1));
	np->host = (char *)malloc((unsigned)(strlen(host) + 1));
	np->symhost = (char *)malloc((unsigned)(strlen(symhost) + 1));
	if(np->name == (char *)0 || np->rempw == (char *)0 ||
			np->locpw == (char *)0 || np->plyport == (char *)0 ||
			np->host == (char *)0 || np->symhost == (char *)0) {
		logf("cannot allocate remote mud data. owie.\n",(char *)0);
		return(1);
	}
	(void)strcpy(np->name,name);
	(void)strcpy(np->rempw,rempw);
	(void)strcpy(np->locpw,locpw);
	(void)strcpy(np->plyport,plyport);
	(void)strcpy(np->host,host);
	(void)strcpy(np->symhost,symhost);

	np->flg = 0;

	/* link on */
	np->next = mud_list;
	mud_list = np;

	return(0);
}




xc_status()
{
	if(xbuf.fd == -1)
		return(1);
	return(0);
}



xc_close()
{
	shutdown(xbuf.fd,2);
	close(xbuf.fd);
	xbuf.fd = -1;
	xbuf.curmp = (Mudent *)0;

	logf("hang up connection\n",(char *)0);
#ifdef	XMITBSD_DEBUG
	(void)printf("<<<dis-connected>>>\n");
#endif
	return(0);
}




char	*
xc_error()
{
	return("no error");
}



xc_sbread(sb)
Sbuf	*sb;
{
	if(xbuf.fd == -1 || sb == (Sbuf *)0)
		return(0);

	sbuf_reset(sb);
	while(1) {

		/* buffer empty ? fill. */
		if(xbuf.ibc <= 0) {
			fd_set	msk;
			struct	timeval	timo;

			FD_ZERO(&msk);
			FD_SET(xbuf.fd,&msk);
			timo.tv_usec = 0;
			if(xbuf.curmp != (Mudent *)0)
				timo.tv_sec = xbuf.curmp->timot;
			else
				timo.tv_sec = DEFAULT_TIMEOUT;

			if(select(xbuf.fd + 1,&msk,0,0,&timo) != 1) {
				logf("timeout\n",(char *)0);
				return(0);
			}

			xbuf.ibc = read(xbuf.fd,xbuf.ibuf,sizeof(xbuf.ibuf));
			if(xbuf.ibc <= 0)
				return(0);
			xbuf.ibp = xbuf.ibuf;
		}

		/* end of line? */
		if(*xbuf.ibp == '\n') {
			sbuf_put('\0',sb);
			xbuf.ibp++;
			xbuf.ibc--;
			return(1);
		}

		/* put. */
		sbuf_put(*xbuf.ibp,sb);
		xbuf.ibp++;
		xbuf.ibc--;
	}
}




xc_flush()
{
	fd_set	msk;
	struct	timeval	timo;
	int	twrt;

	FD_ZERO(&msk);
	FD_SET(xbuf.fd,&msk);
	timo.tv_usec = 0;
	if(xbuf.curmp != (Mudent *)0)
		timo.tv_sec = xbuf.curmp->timot;
	else
		timo.tv_sec = DEFAULT_TIMEOUT;

	if(select(xbuf.fd + 1,0,&msk,0,&timo) != 1) {
		logf("write timeout\n",(char *)0);
		return(1);
	}

	twrt = xbuf.obp - xbuf.obuf;

	if(write(xbuf.fd,xbuf.obuf,twrt) != twrt)
		return(1);
	xbuf.obp = xbuf.obuf;
	return(0);
}




/* VARARGS */
xc_write(va_alist)
va_dcl
{
	char	*s;
	va_list	ap;


	va_start(ap);
	while((s = va_arg(ap,char *)) != (char *)0) {
		while(*s) {
			if((xbuf.obp - xbuf.obuf) >= sizeof(xbuf.obuf))
				if(xc_flush())
					return(1);

			*xbuf.obp++ = *s++;
		}
	}
	va_end(ap);
	return(0);
}

/* Maintain a list of MUD-names we've heard about. */
static Mudent	*known_muds = 0;
static int	restrict_known = 0;	/* Don't restrict to just known
					   MUDs. */

static Mudent *
isknownmud(mud)
char	*mud;
{
	Mudent	*temp, *pos;

	temp = getmudent(mud);
	if (temp != NULL)
		return temp;
	pos = known_muds;
	while (pos != NULL) {
		if (!strcmp(pos->name, mud))
			return pos;
		pos = pos->next;
	}
	return (Mudent *) 0;
}

int
xmit_def_knownmud(mud)
char	*mud;
{
	Mudent	*mp;

	restrict_known = 1;

	mp = isknownmud(mud);
	if (mp != 0)
		return 0;
	
	mp = (Mudent *) malloc(sizeof(Mudent));
	if (mp == 0) {
		logf("couldn't alloc memory for knownmud mudent\n", (char *)0);
		return 1;
	}
	mp->name = (char *)malloc((unsigned)(strlen(mud) + 1));
	if (mp->name == 0) {
		free(mp);
		logf("couldn't alloc memory for knownmud name\n", (char *)0);
		return 1;
	}
	(void)strcpy(mp->name, mud);
	mp->next = known_muds;
	known_muds = mp;

	return 0;
}

/* A given MUD name is okay if:
	- we don't have MUD security enabled (we have specified no defknownmuds)
	- it's a MUD we connect to
	- it's a MUD listed in a _mudconfig defknownmud
or	- it's us.
*/
int
xmit_okay_remote(name)
char	*name;
{
	if (!restrict_known)
		return 1;
	if (isknownmud(name) != 0)
		return 1;
	if (!strcmp(name, mud_getname()))
		return 1;
	return 0;
}