/*
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;
}