untermud/DOC/
untermud/DOC/U/
untermud/DOC/U/U-examples/
untermud/DOC/internals/
untermud/DOC/wizard/
untermud/MISC/
untermud/MISC/dbchk/
untermud/RWHO/
untermud/RWHO/rwhod/
/*
        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;
}