/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/
/* configure all options BEFORE including system stuff. */
#include "config.h"
#include "mud.h"
#include "vars.h"
#include "RWHO/rwho.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
/*
as always the network code comprises the lioness' share of the MUD.
this module supports connection maintenance and buffering for
berkeley-style tcp/ip sockets. each connection has a pair of
buffers allocated, and once it has been authenticated as a given
player's connection, an entry into a player-name hash table is
made for faster access.
the manner in which buffers are shutdown is bizarre, but it is
all handled in io_sync() to prevent writes into buffers that have
been freed. this way buffers *WILL* hang around 'till the end of
the play/run.
*/
/* you can change PHSIZ, but don't mess with PHMAG */
#define PHSIZ 31 /* width of internal name hash table */
/* Iob flags */
#define IOBOK 001 /* OK/logged in player connection */
#define IOBKILL 002 /* kill this IOB at sync time - it's dead */
#define IOBERR 004 /* ignore this IOB - it is f***ed up. */
#define IOBFL 010 /* Too many chars. Flush it. */
typedef struct {
int flg; /* flags */
char *host; /* name of player's host */
time_t ltim; /* last input time */
time_t ctim; /* connect time */
char who[MAXOID]; /* player object ID */
SOCKET fd; /* file desc */
char *obuf; /* malloced output buffer */
char *op; /* output buf ptr */
char *ibuf; /* malloced input buffer */
char *ip; /* input buf ptr */
int ic; /* input byte cnt */
} Iob;
/*
player-name to Iob resolution map
an entry is made in this when the player is authenticated by the
login(), and is subsequently used to quickly map names to Iobs.
the mapping is destroyed when the iob is dropped by iobdrop().
don't mess with this code - it's icky.
*/
typedef struct pmap {
Iob *iob;
struct pmap *n;
} Pmap;
static Pmap *pmaptab[PHSIZ];
static Iob **iobtab; /* active Iob table */
static Iob **WHOlist; /* Ordered WHO list */
static int WHOlast = -1; /* Last active iob in WHOlist */
static Iob *lastiob = (Iob *) 0;
static int onewrt = 0; /* optimization */
static int iobtabsiz; /* top bound of Iob table */
static int curriob; /* highest Iob in use */
static fd_set liveset; /* set of live Iobs for select() */
static struct timeval timo; /* timeout value */
static SOCKET serfd; /* inter server service port */
static SOCKET plyfd; /* main player service port */
struct sockaddr_in addr; /* address of newly connected */
static int sport = NET_SRVPRT; /* service port # */
static int pport = NET_PLYPRT; /* play port # */
static int net_initted = 0;
static int nxtwhoiobptr; /* used for programmed Iob traverse */
static char *log_connect (SOCKET fd, char *desc);
/*
set up the iob tables, iob maps, sockets, the whole bit
*/
int io_init ()
{
int x;
#ifndef WIN32
signal (SIGPIPE, SIG_IGN);
#endif
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
timo.tv_sec = cron_quantum;
timo.tv_usec = 0;
/*
if you ain't got dtablesize(), fudge this with whatever
your systems max file descriptor value is. erring on the
high side will waste a little memory is all.
*/
iobtabsiz = getdtablesize ();
/* initialize the Iob hash table */
iobtab = (Iob **) malloc ((unsigned) (sizeof (Iob *) * iobtabsiz));
if (iobtab == (Iob **) 0)
return (1);
WHOlist = (Iob **) malloc ((unsigned) (sizeof (Iob *) * iobtabsiz));
if (WHOlist == (Iob **) 0)
return (1);
/* zero iob table */
for (x = 0; x < iobtabsiz; x++)
iobtab[x] = (Iob *) 0;
/* zero player to Iob hash table */
for (x = 0; x < PHSIZ; x++)
pmaptab[x] = (Pmap *) 0;
/* OPEN INTER SERVER FILE DESCRIPTOR AND BIND IT */
if ((serfd = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
return (1);
x = 1;
setsockopt (serfd, SOL_SOCKET, SO_REUSEADDR, (char *) &x, sizeof (x));
addr.sin_port = htons (sport);
if (bind (serfd, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR) {
log_printf ("cannot bind socket: ", (char *) -1, "\n", 0);
return (1);
}
if (listen (serfd, 5) == SOCKET_ERROR) {
log_printf ("cannot listen at socket: ", (char *) -1, "\n", 0);
return (1);
}
/* OPEN PLAYER ACCESS FILE DESCRIPTOR AND BIND IT */
if ((plyfd = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
return (1);
x = 1;
setsockopt (plyfd, SOL_SOCKET, SO_REUSEADDR, (char *) &x, sizeof (x));
addr.sin_port = htons (pport);
if (bind (plyfd, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR) {
log_printf ("cannot bind socket: ", (char *) -1, "\n", 0);
return (1);
}
if (listen (plyfd, 5) == SOCKET_ERROR) {
log_printf ("cannot listen at socket: ", (char *) -1, "\n", 0);
return (1);
}
/* VITAL! */
FD_SET (plyfd, &liveset);
FD_SET (serfd, &liveset);
curriob = plyfd;
net_initted++;
return (0);
}
/*
disconnect an Iob (low level)
*/
static void iobdrop (Iob * ip)
{
int x;
Pmap *pp;
Pmap *pr;
if (ip == (Iob *) 0)
return;
/* Blow it out of WHO list */
for (x = 0; x <= WHOlast; x++) {
if (WHOlist[x] == ip) {
if (x < WHOlast)
bcopy (&WHOlist[x + 1], &WHOlist[x], (WHOlast - x) * sizeof (Iob *));
else
WHOlist[x] = (Iob *) 0;
WHOlast--;
break;
}
}
if (ip->who[0]) {
plogf ("DISCONNECT %s on %d from %s\n", ip->who, ip->fd,
ip->host ? ip->host : "unknown");
#ifdef USE_RWHO
rwhocli_userlogout (ip->who);
#endif
} else
plogf ("DISCONNECT %d from %s\n", ip->fd,
ip->host ? ip->host : "unknown");
for (x = 0; x < iobtabsiz; x++) {
if (iobtab[x] == ip) {
iobtab[x] = (Iob *) 0;
break;
}
}
/* unlink player map hash. this is somewhat convoluted. */
for (x = 0; x < PHSIZ; x++) {
pp = pmaptab[x];
while (pp != (Pmap *) 0) {
if (pp->iob == ip && pp == pmaptab[x]) {
pr = pp;
pmaptab[x] = pp->n;
free ((mall_t) pr);
break;
} else {
if (pp->n != (Pmap *) 0 && pp->n->iob == ip) {
pr = pp->n;
pp->n = pp->n->n;
free ((mall_t) pr);
break;
}
}
pp = pp->n;
}
}
/* adjust top iob in use count */
if (ip->fd == curriob)
curriob--;
/* mark it dead in the live Iob set */
FD_CLR (ip->fd, &liveset);
/* final cleanup */
shutdown (ip->fd, 2);
closesocket (ip->fd);
if (ip->host != (char *) 0)
free ((mall_t) ip->host);
free ((mall_t) ip->ibuf);
free ((mall_t) ip->obuf);
/*
this is done to cause a coredump in case someone's code is
so stupid as to write into an Iob that has been closed down
*/
ip->ibuf = ip->obuf = (char *) 0;
free ((mall_t) ip);
}
/*
tokenize out a single line of text (destructive)
this function handles a buffer of player input, returning a sequence of
lines - it may move data around in the buffer, and may alter the content
of the buffer as it does so. this handles cases where a line may be
broken into two packets by the network - it re-assembles them efficiently.
*/
static char *iobgl (Iob * i)
{
char *op;
int ic;
while (i->ic > 0 && (*i->ip == '\n' || *i->ip == '\r')) {
*i->ip++ = '\0';
i->ic--;
}
if ((ic = i->ic) <= 0) {
i->ip = i->ibuf;
i->ic = 0;
return ((char *) 0);
}
op = i->ip;
while (1) {
if (*i->ip == '\r' && *(i->ip + 1) == '\n' && ic > 1) {
*i->ip++ = '\0';
*i->ip++ = '\0';
if ((i->ic = ic - 2) < 1)
i->ip = i->ibuf;
if (i->flg & IOBFL) { /* If we're supposed to flush */
i->flg &= ~IOBFL;
return ((char *) 0);
} else {
return (op);
}
}
if ((*i->ip == '\n' || *i->ip == '\r') && ic > 0) {
*i->ip++ = '\0';
if ((i->ic = --ic) < 1)
i->ip = i->ibuf;
if (i->flg & IOBFL) { /* If we're supposed to flush */
i->flg &= ~IOBFL;
return ((char *) 0);
} else {
return (op);
}
}
/* nothing left in buffer, but no newline */
if (--ic <= 0) {
if (i->ic >= MUDBUF - 1) { /* Too Much! */
i->ip = i->ibuf;
i->ic = 0;
/* Are we already flushing? */
if (i->flg & IOBFL) {
return ((char *) 0);
} else {
/* set up to flush until EOLN */
i->flg |= IOBFL;
*(i->ibuf + (MUDBUF - 1)) = '\0';
return (i->ibuf);
}
}
/* shift down */
bcopy (op, i->ibuf, i->ic);
/* resync */
i->ip = i->ibuf;
/* i->ic = 0; Why is this here? -- Andrew */
return ((char *) 0);
}
if (!isprint (*i->ip))
*i->ip = ' ';
i->ip++;
}
}
/*
flush a single Iob
doing the select there significantly increases the system call overhead
of the MUD, as opposed to doing nonblocking I/O. but that's life.
*/
static int iobflush (Iob * ip)
{
static char buff_over[] = "\n<buffer overrun>\n";
fd_set wfd;
struct timeval wtimo;
int xx = ip->op - ip->obuf;
int wr;
if (ip->flg & IOBERR)
return (1);
FD_ZERO (&wfd);
FD_SET (ip->fd, &wfd);
wtimo.tv_sec = 0;
wtimo.tv_usec = 800;
if (select (ip->fd + 1, (fd_set *) 0, &wfd, (fd_set *) 0, &wtimo) != 1 ||
!FD_ISSET (ip->fd, &wfd)) {
if (xx < MUDBUF)
return (0);
/* scrubba buffa */
bcopy (buff_over, ip->obuf, sizeof (buff_over));
ip->op = ip->obuf + sizeof (buff_over);
return (0);
}
if ((wr = send (ip->fd, ip->obuf, xx, 0)) != xx) {
/* partial write. god damn */
if (wr > 0) {
bcopy (ip->obuf + wr, ip->obuf, xx - wr);
ip->op -= wr;
return (0);
}
if (wr == SOCKET_ERROR && GETERROR == EWOULDBLOCK) {
if (xx >= MUDBUF) {
/* scrubba buffa */
bcopy (buff_over, ip->obuf, sizeof (buff_over));
ip->op = ip->obuf + sizeof (buff_over);
}
return (0);
}
/* fukit */
ip->flg |= IOBERR;
return (1);
}
ip->op = ip->obuf;
return (0);
}
/* VARARGS1 */
void say (char *who, ...)
{
Pmap *p;
char *s;
va_list ap;
if (who == (char *) 0 || who[0] == '\0' || !net_initted)
return;
for (p = pmaptab[objid_hash (who, PHSIZ)]; p != (Pmap *) 0; p = p->n) {
/* wrong guy */
if ((p->iob->flg & IOBERR) || strcmp (p->iob->who, who))
continue;
if (lastiob != p->iob) {
lastiob = p->iob;
onewrt++;
}
va_start (ap, who);
while ((s = va_arg (ap, char *)) != (char *) 0) {
while (*s) {
if ((p->iob->op - p->iob->obuf) > MUDBUF - 1) {
if (iobflush (p->iob))
goto dropthru;
} else {
*p->iob->op++ = *s++;
}
}
}
dropthru:
va_end (ap);
}
}
#ifdef CONNONLY
int playerconn (char *who)
{
Pmap *p;
if (who == (char *) 0 || who[0] == '\0' || !net_initted)
return (0);
for (p = pmaptab[objid_hash (who, PHSIZ)]; p != (Pmap *) 0; p = p->n) {
if ((p->iob->flg & IOBERR) || strcmp (p->iob->who, who))
/* wrong guy */
continue;
else
return (1);
}
return (0);
}
#endif
void io_logoff (char *who)
{
Pmap *p;
if (who == (char *) 0 || who[0] == '\0' || !net_initted)
return;
p = pmaptab[objid_hash (who, PHSIZ)];
while (p != (Pmap *) 0) {
if (strcmp (p->iob->who, who)) {
p = p->n;
continue;
}
p->iob->flg |= IOBKILL;
onewrt = 2;
p = p->n;
}
}
/* VARARGS1 */
void iobsay (Iob * ip, ...)
{
char *s;
va_list ap;
if (ip->flg & IOBERR)
return;
if (lastiob != ip) {
lastiob = ip;
onewrt++;
}
va_start (ap, ip);
while ((s = va_arg (ap, char *)) != (char *) 0) {
while (*s) {
if ((ip->op - ip->obuf) > MUDBUF - 1) {
if (iobflush (ip))
return;
} else {
*ip->op++ = *s++;
}
}
}
va_end (ap);
}
/*
flush the connected (valid) Iobs
*/
void io_sync ()
{
int n;
time_t now = (time_t) 0;
if (!onewrt || !net_initted)
return;
/* WARNING!!! the calls to goodbye() actually may make more
Iob writes as a result of a player hangup! Do NOT do much
in goodbye!() or madness may result. */
if (onewrt == 1 && lastiob != (Iob *) 0) {
if (iobflush (lastiob)) {
if (lastiob->flg & IOBOK)
goodbye (lastiob->who);
iobdrop (lastiob);
return;
}
if (lastiob->flg & IOBERR || lastiob->flg & IOBKILL) {
if (lastiob->flg & IOBOK)
goodbye (lastiob->who);
iobdrop (lastiob);
}
return;
}
for (n = 0; n < iobtabsiz; n++) {
if (iobtab[n] != (Iob *) 0) {
if (iobtab[n]->op > iobtab[n]->obuf) {
if (iobflush (iobtab[n])) {
if (iobtab[n]->flg & IOBOK)
goodbye (iobtab[n]->who);
iobdrop (iobtab[n]);
continue;
}
}
if (iobtab[n]->flg & IOBKILL || iobtab[n]->flg & IOBERR) {
if (iobtab[n]->flg & IOBOK)
goodbye (iobtab[n]->who);
iobdrop (iobtab[n]);
continue;
}
/* timeout logins */
if (!(iobtab[n]->flg & IOBOK)) {
if (now == (time_t) 0)
time (&now);
if (now - iobtab[n]->ctim > 80)
iobdrop (iobtab[n]);
}
}
}
onewrt = 1;
lastiob = (Iob *) 0;
}
/*
wrapper around the player authentication routine. said routine will
return a 1 if the login is valid, a 0 if it is not. if the login is
OK, we then fiddle some Iob values and make a hash-table map entry.
if login() returns -1, then we are to close the connection.
*/
static void io_dologin (Iob * bp, char *line)
{
Pmap *pm;
int hv;
FILE *cfl;
char cbuf[BUFSIZ];
if (newusers) {
/* Check for new user login */
if (!strncmp ("new", line, 3) || !strncmp ("New", line, 3)) {
hv = newlogin (line, bp->who);
} else {
hv = login (line, bp->who);
}
} else {
if (!strncmp ("new", line, 3) || !strncmp ("New", line, 3)) {
iobsay (bp, "New users are not enabled.\n", (char *) 0);
return;
}
hv = login (line, bp->who);
}
if (hv == 2) {
iobsay (bp, "Your new player ID is ", bp->who,
". Don't forget it, you need it to log back in.\n", (char *) 0);
/* Show the new user information file */
if ((cfl = fopen ("NEWS/newuser.txt", "rb")) != (FILE *) 0) {
while (fgets (cbuf, sizeof (cbuf), cfl) != (char *) 0)
iobsay (bp, cbuf, (char *) 0);
(void) fclose (cfl);
}
}
if (hv == 0) {
iobsay (bp,
"Either that object does not exist, or the password was incorrect.\n",
(char *) 0);
/*log_printf("badlogin: ",line,"\n",(char *)0); */
plogf ("badlogin: %s on %d from %s\n", line, bp->fd,
bp->host ? bp->host : "unknown");
return;
}
if (hv == -1) {
plogf ("killconnection: '%s' on %d from %s\n", line, bp->fd,
bp->host ? bp->host : "unknown");
bp->flg |= IOBKILL;
onewrt = 2;
return;
}
bp->flg |= IOBOK;
/* Stuff this Iob into the WHO list */
WHOlist[++WHOlast] = bp;
/* now add a pointer to the character's name in the Iob hash */
pm = (Pmap *) malloc (sizeof (Pmap));
if (pm == (Pmap *) 0)
fatal ("out of memory building new connection\n", (char *) 0);
pm->iob = bp;
pm->n = pmaptab[(hv = objid_hash (bp->who, PHSIZ))];
pmaptab[hv] = pm;
plogf ("CONNECT %s on %d from %s\n", bp->who, bp->fd,
bp->host ? bp->host : "unknown");
welcome (bp->who);
#ifdef USE_RWHO
rwhocli_userlogin (bp->who, ut_name (bp->who), bp->ctim);
#endif
}
/*
main I/O loop - listens for new connections, accepts them, validates
them, reads input, and dispatches it.
*/
int io_loop ()
{
fd_set redy;
fd_set xcpt;
Iob *bp;
SOCKET n;
int seld;
int rd;
char *lp;
time_t now;
char *host;
if (!net_initted)
return (-1);
/* sleep quantum may have changed */
timo.tv_sec = cron_quantum;
bcopy (&liveset, &redy, sizeof (redy));
bcopy (&liveset, &xcpt, sizeof (xcpt));
if ((seld =
select (curriob + 1, &redy, (fd_set *) 0, &xcpt,
&timo)) == SOCKET_ERROR) {
if (GETERROR != EINTR) {
log_printf ("select in loop failed: ", (char *) -1, "\n", 0);
return (1);
}
}
/* yawn */
if (seld <= 0)
return (0);
/* start the clock */
(void) time (&now);
/* new SERVER TO SERVER connection */
if (FD_ISSET (serfd, &redy)) {
n = accept (serfd, (struct sockaddr *) 0, (int *) 0);
/* xact_in() handles *everything* including closing fd */
if (n != INVALID_SOCKET) {
#ifdef SO_LINGER
#ifdef hpux
/*
* HP-UX (release 7.0 B on a 9000/330, at least) claims to default to no-
* lingering and, in any case, has a different syntax for passing the
* linger value to setsockopt(). Don't use it.
* --- warlock
*/
#undef SO_LINGER
#endif /* hpux */
/*
struct linger ling;
ling.l_onoff = 0;
ling.l_linger = 0;
setsockopt(n,SOL_SOCKET,SO_LINGER,&ling,sizeof(ling));
*/
#endif
host = log_connect (n, "server");
if (host != (char *) 0) /* Toss the host in this case */
free ((mall_t) host);
xact_in (n);
}
}
/* new PLAYER connection */
if (FD_ISSET (plyfd, &redy)) {
n = accept (plyfd, (struct sockaddr *) 0, (int *) 0);
if (n != INVALID_SOCKET) {
#ifdef SO_LINGER
/*
FIXME
struct linger ling;
ling.l_onoff = 0;
ling.l_linger = 0;
setsockopt(n,SOL_SOCKET,SO_LINGER,&ling,sizeof(ling));
*/
#endif
#ifdef WIN32
unsigned long flags = 1;
#endif
host = log_connect (n, "user");
#ifdef WIN32
if (ioctlsocket (n, FIONBIO, &flags)) {
#else
if (fcntl (n, F_SETFL, FNDELAY)) {
#endif
log_printf ("cannot set NDELAY: ", (char *) -1, "\n", 0);
goto mainloop;
}
bp = (Iob *) malloc (sizeof (Iob));
if (bp == (Iob *) 0) {
log_printf ("cannot alloc Iob: ", (char *) -1, "\n", 0);
return (-1);
}
bp->host = host;
bp->flg = 0;
bp->who[0] = '\0';
bp->fd = n;
bp->ctim = now;
if ((bp->ibuf = (char *) malloc (MUDBUF)) == (char *) 0)
return (-1);
bp->ip = bp->ibuf;
bp->ic = 0;
if ((bp->obuf = (char *) malloc (MUDBUF)) == (char *) 0)
return (-1);
bp->op = bp->obuf;
if (n > curriob)
curriob = n;
/* enter it into our live set */
FD_SET (n, &liveset);
#ifdef WIN32
{
int x;
for (x = 0; x < iobtabsiz; x++) {
if (iobtab[x] == NULL) {
iobtab[x] = bp;
break;
}
}
}
#else
iobtab[n] = bp;
#endif
/* I added this next little block so I could display
a file before the character logs in. Ed Hand 6/26/91 */
{
FILE *cfl;
char cbuf[BUFSIZ];
if ((cfl = fopen ("NEWS/connect.txt", "rb")) != (FILE *) 0) {
while (fgets (cbuf, sizeof (cbuf), cfl) != (char *) 0)
iobsay (bp, cbuf, (char *) 0);
(void) fclose (cfl);
}
}
iobsay (bp, "If you have a character already,\n",
"type: c[onnect] objectid password\n", (char *) 0);
if (newusers) {
iobsay (bp, "To create a new character,\n", (char *) 0);
iobsay (bp, "type: new charname password\n", (char *) 0);
} else {
iobsay (bp, "New users are not enabled.\n", (char *) 0);
}
}
}
mainloop:
/* check input on existing fds. */
#ifdef WIN32
for (n = 0; n < iobtabsiz; n++) {
#else
for (n = 0; n <= curriob; n++) {
#endif
bp = iobtab[n];
if (bp == NULL)
continue;
if (FD_ISSET (bp->fd, &redy)) {
rd = recv (bp->fd, bp->ibuf + bp->ic, MUDBUF - bp->ic - 1, 0);
if (rd <= 0) {
bp->flg |= IOBERR | IOBKILL;
onewrt = 2;
continue;
}
/* increment count of bytes in buffer */
bp->ic += rd;
/* adjust last active time */
bp->ltim = now;
/* process input based on state of connection */
while ((lp = iobgl (bp)) != (char *) 0) {
if (bp->flg & IOBOK)
run (bp->who, bp->who, lp, 0, (char **) 0, 1);
else
io_dologin (bp, lp);
}
continue;
}
/* lastly, check exceptions in case of no input */
if (FD_ISSET (bp->fd, &xcpt)) {
if (bp->fd == serfd)
fatal ("server service fd died!!\n", (char *) 0);
if (bp->fd == plyfd)
fatal ("player service fd died!!\n", (char *) 0);
/* default case */
if (bp != (Iob *) 0) {
bp->flg |= IOBERR | IOBKILL;
onewrt = 2;
}
continue;
}
}
/* e! */
return (0);
}
/* ARGSUSED */
int cmd__netconfig (int argc, char *argv[], char *who, char *aswho)
{
static char *nactm = "network layer is already active.\n";
static char *badp = "invalid port number.\n";
/* configure service port */
if (!strcmp (argv[1], "playport")) {
int tmpx;
if (net_initted) {
log_printf (nactm, (char *) 0);
return (UERR_FATAL);
}
if (argc < 3 || (tmpx = atoi (argv[2])) <= 0) {
log_printf (badp, (char *) 0);
return (UERR_FATAL);
}
pport = tmpx;
log_printf ("player port is #", argv[2], "\n", (char *) 0);
return (UERR_NONE);
}
/* configure service port */
if (!strcmp (argv[1], "servport")) {
int tmpx;
if (net_initted) {
log_printf (nactm, (char *) 0);
return (UERR_FATAL);
}
if (argc < 3 || (tmpx = atoi (argv[2])) <= 0) {
log_printf (badp, (char *) 0);
return (UERR_FATAL);
}
sport = tmpx;
log_printf ("server port is #", argv[2], "\n", (char *) 0);
return (UERR_NONE);
}
/* set time-out of select (to match quantum?) */
if (!strcmp (argv[1], "looptime")) {
int tmpx;
if (argc < 3 || (tmpx = atoi (argv[2])) <= 0) {
log_printf (badp, (char *) 0);
return (UERR_BADPARM);
}
timo.tv_sec = tmpx;
log_printf ("loop timeout is ", argv[2], " seconds\n", (char *) 0);
return (UERR_NONE);
}
if (!strcmp (argv[1], "help")) {
say (who, argv[0], " playport port-number\n", (char *) 0);
say (who, argv[0], " servport port-number\n", (char *) 0);
say (who, argv[0], " looptime seconds\n", (char *) 0);
return (UERR_NONE);
}
log_printf ("_netconfig: I don't understand ", argv[1], "\n", (char *) 0);
return (UERR_BADPARM);
}
static void cmd_WHO__format (char *buffer, int flg, char *name,
time_t curtime, time_t ltime, char *objname, char *host)
{
int ct_hours, ct_mins, ct_secs, lt_hours, lt_mins, lt_secs;
char oidb[MAXOID + 3];
ct_hours = curtime / 3600;
ct_mins = (curtime % 3600) / 60;
ct_secs = curtime % 60;
lt_hours = ltime / 3600;
lt_mins = (ltime % 3600) / 60;
lt_secs = ltime % 60;
if (host == (char *) 0)
host = "unknown";
if (flg) { /* Wiz WHO thang */
oidb[0] = '(';
strncpy (oidb + 1, objname, MAXOID);
strcat (oidb, ")");
sprintf (buffer,
"%-17.17s %3d:%02d:%02d %3d:%02d:%02d %-18.18s %-18.18s\n",
name,
ct_hours, ct_mins, ct_secs, lt_hours, lt_mins, lt_secs, oidb, host);
} else { /* Regular WHO */
sprintf (buffer, "%-17.17s %3d:%02d:%02d %3d:%02d:%02d (%s)\n",
name, ct_hours, ct_mins, ct_secs, lt_hours, lt_mins, lt_secs, objname);
}
}
/* TCP version of WHO */
/* ARGSUSED */
int cmd_WHO (int argc, char *argv[], char *who, char *aswho)
{
int x;
int jj;
int wiz;
time_t now;
char xuf[180];
if (!net_initted)
return (1);
wiz = ut_flagged (aswho, var_wiz);
(void) time (&now);
say (who, "Player Name On For Idle\n", (char *) 0);
if (argc < 2) {
for (x = 0; x <= WHOlast; x++) {
cmd_WHO__format (xuf, wiz,
ut_name (WHOlist[x]->who),
(now - WHOlist[x]->ctim),
(now - WHOlist[x]->ltim), WHOlist[x]->who, WHOlist[x]->host);
say (who, xuf, (char *) 0);
}
say (who, "There are ", itoa (WHOlast + 1, xuf, 10),
" players connected.\n", (char *) 0);
return (UERR_NONE);
}
for (jj = 1; jj < argc; jj++) {
char *na;
for (x = 0; x <= WHOlast; x++) {
na = ut_name (WHOlist[x]->who);
if (na != (char *) 0 && matchstr (na, argv[jj], 0) != 0) {
cmd_WHO__format (xuf, wiz,
ut_name (WHOlist[x]->who),
(now - WHOlist[x]->ctim),
(now - WHOlist[x]->ltim), WHOlist[x]->who, WHOlist[x]->host);
say (who, xuf, (char *) 0);
}
}
say (who, "There are ", itoa (WHOlast + 1, xuf, 10),
" players connected.\n", (char *) 0);
}
return (UERR_NONE);
}
/* reset pointer to live Iobs for programmed traverse */
void io_rstnxtwho (void)
{
nxtwhoiobptr = 0;
}
/* programmed traverse of object-IDs of logged-in players */
char *io_nxtwho (time_t * timp)
{
#ifdef WIN32
if (nxtwhoiobptr > iobtabsiz || !net_initted)
#else
if (nxtwhoiobptr > curriob || !net_initted)
#endif
return ((char *) 0);
#ifdef WIN32
while (nxtwhoiobptr < iobtabsiz) {
#else
while (nxtwhoiobptr <= curriob) {
#endif
if (iobtab[nxtwhoiobptr] != (Iob *) 0 &&
(iobtab[nxtwhoiobptr]->flg & IOBOK) &&
(iobtab[nxtwhoiobptr]->flg & (IOBKILL | IOBERR)) == 0 &&
iobtab[nxtwhoiobptr]->who[0] != '\0') {
if (timp != (time_t *) 0)
*timp = iobtab[nxtwhoiobptr]->ctim;
return (iobtab[nxtwhoiobptr++]->who);
}
nxtwhoiobptr++;
}
return ((char *) 0);
}
/* Log (via logf) the essential statistics of a new connection. */
static char *log_connect (SOCKET fd, char *desc)
{
struct sockaddr_in sin;
char *t;
char *name;
int sin_len;
#ifndef NO_HUGE_RESOLVER_CODE
struct hostent *hp;
#endif
sin_len = sizeof (sin);
if (getpeername (fd, (struct sockaddr *) &sin, &sin_len) == SOCKET_ERROR) {
plogf ("ACCEPT [%s] getpeername failed! on %d\n", desc, fd);
return ((char *) 0);
}
#ifndef NO_HUGE_RESOLVER_CODE
hp = gethostbyaddr ((char *) &sin.sin_addr, sizeof (struct in_addr),
AF_INET);
if (hp != (struct hostent *) 0) {
plogf ("ACCEPT [%s] from %s on %d\n", desc, hp->h_name, fd);
if ((name = (char *) malloc (strlen (hp->h_name) + 1)) == (char *) 0)
return ((char *) 0);
strcpy (name, hp->h_name);
return (name);
}
#endif
t = inet_ntoa (sin.sin_addr);
if (t == (char *) 0) {
plogf ("ACCEPT [%s] inet_ntoa failed! on %d\n", fd);
return ((char *) 0);
}
plogf ("ACCEPT [%s] from %s on %d\n", desc, t, fd);
if ((name = (char *) malloc (strlen (t) + 1)) == (char *) 0)
return ((char *) 0);
strcpy (name, t);
return (name);
}