/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/
/* configure all options BEFORE including system stuff. */
#include "config.h"
#include "mud.h"
#include "xact.h"
#include "sbuf.h"
typedef void (*sighandler_t) (int);
#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 {
SOCKET fd;
char ibuf[XMBUFSIZ];
char *ibp;
int ibc;
char obuf[XMBUFSIZ];
char *obp;
Mudent *curmp;
} Cnxt;
int xc_write (char *, ...);
static Mudent *getmudent(char *mud);
static Mudent *isknownmud(char *mud);
static Mudent *mud_list;
static Cnxt xbuf;
static Mudent *lastmud = (Mudent *) 0;
/* Look up the remote MUD and set the context up right */
int set_remmud (char *nam)
{
Mudent *mp;
if ((mp = getmudent (nam)) == (Mudent *) 0)
return (1);
lastmud = xbuf.curmp = mp;
return (0);
}
int xc_open (char *mud)
{
Mudent *mp;
#ifdef WIN32
unsigned long flags = 1;
#endif
if ((mp = getmudent (mud)) == (Mudent *) 0) {
log_printf ("unknown mud ", mud, "\n", (char *) 0);
return (-1);
}
if ((xbuf.fd = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
perror ("socket");
return (-1);
}
#ifdef WIN32
ioctlsocket (xbuf.fd, FIONBIO, &flags);
#else
fcntl (xbuf.fd, F_SETFL, FNDELAY);
#endif
/* this may EWOULDBLOCK, which is OK */
if (connect (xbuf.fd, (struct sockaddr *) &(mp->addr),
sizeof (struct sockaddr_in)) == SOCKET_ERROR) {
fd_set msk;
sighandler_t sigsaved;
struct timeval timo;
#ifdef EINPROGRESS
if (GETERROR != EWOULDBLOCK && GETERROR != EINPROGRESS) {
#else
if (GETERROR != EWOULDBLOCK) {
#endif
shutdown (xbuf.fd, 2);
closesocket (xbuf.fd);
#ifdef XMITBSD_DEBUG
perror ("connect");
#endif
log_printf ("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) {
shutdown (xbuf.fd, 2);
closesocket (xbuf.fd);
#ifdef XMITBSD_DEBUG
perror ("connect failed (timeout?)");
#endif
log_printf ("connection to ", mud, " timeout ", (char *) -1, "\n",
(char *) 0);
return (-1);
}
/* Probe the fd to see if the connect() worked */
#ifndef WIN32
sigsaved = signal (SIGPIPE, SIG_IGN);
#endif
if (write (xbuf.fd, "", 0) < 0) {
#ifndef WIN32
signal (SIGPIPE, sigsaved);
#endif
return (-1);
}
#ifndef WIN32
signal (SIGPIPE, sigsaved);
#endif
}
#ifdef XMITBSD_DEBUG
(void) printf ("<<<connected>>>\n");
#endif
log_printf ("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);
}
int xc_initfd (SOCKET fd)
{
#ifdef WIN32
unsigned long flags = 1;
#endif
xbuf.fd = fd;
xbuf.ibp = xbuf.ibuf;
xbuf.ibc = 0;
xbuf.obp = xbuf.obuf;
*xbuf.ibp = *xbuf.obp = '\0';
#ifdef WIN32
ioctlsocket (xbuf.fd, FIONBIO, &flags);
#else
fcntl (xbuf.fd, F_SETFL, FNDELAY);
#endif
return (0);
}
/* Send out an OIF Level 2 greet string to the current mud */
int 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 */
int check_rempwd (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 ()
{
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 (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.
*/
int xmit_def_mudent (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) {
log_printf ("unknown host ", host, "\n", (char *) 0);
return (1);
}
(void) bcopy (hp->h_addr, (char *) &tmpa.sin_addr, hp->h_length);
#else
log_printf ("must use 'dot' notation to define host ", host, "\n",
(char *) 0);
return (1);
#endif
} else {
#ifdef WIN32
unsigned long f;
#else
struct in_addr f;
#endif
#ifdef WIN32
if ((f = inet_addr (host)) == INADDR_NONE)
return 1;
#else
if (inet_aton (host, &f) == 0)
return 1;
#endif
bcopy ((char *) &f, (char *) &tmpa.sin_addr, sizeof (f));
}
if ((np = (Mudent *) malloc (sizeof (Mudent))) == (Mudent *) 0) {
log_printf ("cannot allocate remote mud map\n", (char *) 0);
return (1);
}
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) {
log_printf ("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);
}
void xc_close ()
{
shutdown (xbuf.fd, 2);
closesocket (xbuf.fd);
xbuf.fd = -1;
xbuf.curmp = (Mudent *) 0;
log_printf ("hang up connection\n", (char *) 0);
#ifdef XMITBSD_DEBUG
(void) printf ("<<<dis-connected>>>\n");
#endif
}
int xc_sbread (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) {
log_printf ("timeout\n", (char *) 0);
return (0);
}
xbuf.ibc = recv (xbuf.fd, xbuf.ibuf, sizeof (xbuf.ibuf), 0);
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--;
}
}
int 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) {
log_printf ("write timeout\n", (char *) 0);
return (1);
}
twrt = xbuf.obp - xbuf.obuf;
if (send (xbuf.fd, xbuf.obuf, twrt, 0) != twrt)
return (1);
xbuf.obp = xbuf.obuf;
return (0);
}
/* VARARGS */
int xc_write (char *first, ...)
{
char *s = first;
va_list ap;
va_start (ap, first);
do {
while (*s) {
if ((xbuf.obp - xbuf.obuf) >= (int) sizeof (xbuf.obuf))
if (xc_flush ())
return (1);
*xbuf.obp++ = *s++;
}
} while (s = va_arg (ap, char *));
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 (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 (char *mud) {
Mudent *mp;
restrict_known = 1;
mp = isknownmud (mud);
if (mp != 0)
return 0;
mp = (Mudent *) malloc (sizeof (Mudent));
if (mp == 0) {
log_printf ("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);
log_printf ("couldn't alloc memory for knownmud name\n", (char *) 0);
return 1;
}
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 (char *name) {
if (!restrict_known)
return 1;
if (isknownmud (name) != 0)
return 1;
if (!strcmp (name, mud_getname ()))
return 1;
return 0;
}