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

	VMS port by Andrew Molitor.
*/

#ifndef	lint
static	char	RCSid[] = "$Header: /usr/users/mjr/hacks/umud/RCS/xmit_bsd.c,v 1.1 91/06/17 14:37:48 mjr Exp $";
#endif

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


#include	<stdio.h>

#include	"multinet_root:[multinet.include.sys]types.h"
#include	"multinet_root:[multinet.include]netdb.h"
#include	"multinet_root:[multinet.include.sys]socket.h"
#include	"multinet_root:[multinet.include.netinet]in.h"
#include	"multinet_root:[multinet.include.vms]inetiodef.h"


#include	<ctype.h>
#include	<varargs.h>
#include	<ssdef.h>
#include	<libdtdef.h>

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

/*
	We use event flags 62 and 63 here -- one on a timeout timer, one to
synchronize actual network events. We never have more than one event
outstanding here.

	Flags 32, 33, and 34 are used by net_vms.c

	We use timer reqidt 1, for the hell of it. Since we're cancelling this
a lot, you'd best not go using that ID for anything else, eh?

*/
/*
#define	XMITVMS_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;
	short int	iosb[4];
	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;
	long	timo[2];
	int	op;

	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);
	}

	/* Get a connect to the specified MUD. Time out reasonably fast */
	/* Return -1 on error, 0 on success */

	/* Set up a timer */

	op = LIB$K_DELTA_SECONDS;
	lib$cvt_to_internal_time(&op,&(mp->timot),timo);
	sys$setimr(62L,timo,0,1,0);

	/* Queue up the connect request */

	sys$qio(63L,xbuf.fd,IO$_CONNECT,&(xbuf.iosb),0,0,
		&(mp->addr),sizeof(struct sockaddr_in),0,0,0,0);

	/* Wait around for something to happen */

	sys$wflor(32L,0xC0000000L); /* Top two events flags, 62, 63 */
	sys$cantim(1,0);	/* Kill timer now, just in case */

	/* *what* happened? */

	if(xbuf.iosb[0] != SS$_NORMAL){
		/* connect didn't work out. Bag. */

		sys$cancel(xbuf.fd);
		return(-1);
	}

	/* Connect succeeded, cool. */

#ifdef	XMITVMS_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';
	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 = malloc((unsigned)(strlen(name) + 1));
	np->rempw = malloc((unsigned)(strlen(rempw) + 1));
	np->locpw = malloc((unsigned)(strlen(locpw) + 1));
	np->plyport = malloc((unsigned)(strlen(plyport) + 1));
	np->host = malloc((unsigned)(strlen(host) + 1));
	np->symhost = 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	XMITVMS_DEBUG
	(void)printf("<<<dis-connected>>>\n");
#endif
	return(0);
}




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

/* #### *
/* Has to be totally redone. Reads are done with $qio's and timeouts */

xc_sbread(sb)
Sbuf	*sb;
{
	unsigned long eflags;

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

	sbuf_reset(sb);
	while(1) {

		/* buffer empty ? fill. */
		if(xbuf.ibc <= 0) {
			int	op,sec;
			long	timo[2];

			op = LIB$K_DELTA_SECONDS;

			if(xbuf.curmp != (Mudent *)0)
				sec = xbuf.curmp->timot;
			else
				sec = DEFAULT_TIMEOUT;

			/* Start the timer */
			lib$cvt_to_internal_time(&op,&sec,timo);
			sys$setimr(62L,timo,0,1,0);

			/* Queue up a read */
#ifdef XMITVMS_DEBUG
			printf("Queuing a read..\n");
#endif
			xbuf.iosb[0] = xbuf.iosb[1] = 17;
			sys$qio(63L,xbuf.fd,IO$_READVBLK,&(xbuf.iosb),0,0,
				xbuf.ibuf,sizeof(xbuf.ibuf),0,0,0,0);

			/* Wait around for something to happen */

			sys$wflor(32L,0xC0000000L); /* Events flags 62 & 63 */
			sys$cantim(1,0);	/* Kill timer now */
#ifdef XMITVMS_DEBUG
			sys$readef(35L,&eflags);
			printf("Something happened! Flags: %x\n",eflags);
#endif

			/* *what* happened? */

			if(xbuf.iosb[0] != SS$_NORMAL){
				/* read didn't work out. Bag. */
#ifdef XMITVMS_DEBUG
				printf("Read failed. iosb[0]=%d, iosb[1]=%d\n",
					xbuf.iosb[0],xbuf.iosb[1]);
				printf("Buffer contains: %s\n",xbuf.ibuf);
				socket_perror("hep!");
#endif

				sys$cancel(xbuf.fd);
				return(0);
			}
#ifdef XMITVMS_DEBUG
			printf("Read succeeded, read %d chars\n",xbuf.iosb[1]);
#endif
			xbuf.ibc = xbuf.iosb[1];
			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()
{
	long	timo[2];
	int	op,sec;

	op = LIB$K_DELTA_SECONDS;

	if(xbuf.curmp != (Mudent *)0)
		sec = xbuf.curmp->timot;
	else
		sec = DEFAULT_TIMEOUT;

	/* Start the timer */
	lib$cvt_to_internal_time(&op,&sec,timo);
	sys$setimr(62L,timo,0,1,0);

	/* Queue up the write request */

	sys$qio(63L,xbuf.fd,IO$_WRITEVBLK,&(xbuf.iosb),0,0,
		xbuf.obuf,xbuf.obp - xbuf.obuf,0,0,0,0);

	/* Wait around for something to happen */

	sys$wflor(32L,0xC0000000L); /* Top two events flags, 62, 63 */
	sys$cantim(1,0);	/* Kill timer now, just in case */

	/* *what* happened? */

	if(xbuf.iosb[0] != SS$_NORMAL){
		/* write didn't work out. Bag. */

		sys$cancel(xbuf.fd);
		return(1);
	} else {
		xbuf.obp = xbuf.obuf;
	}
	return(0);
}




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

#ifdef XMITVMS_DEBUG
	printf("xc_write: ");
#endif
	va_start(ap);
	while((s = va_arg(ap,char *)) != (char *)0) {
#ifdef XMITVMS_DEBUG
		printf("%s ",s);
#endif
		while(*s) {
			if((xbuf.obp - xbuf.obuf) >= sizeof(xbuf.obuf))
				if(xc_flush())
					return(1);

			*xbuf.obp++ = *s++;
		}
	}
	va_end(ap);
#ifdef XMITVMS_DEBUG
	printf("\n");
#endif
	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 = 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;
}