/* Copyright (C), 1991, Marcus J. Ranum. All rights reserved. to make: cc [-DSETSID -DDAEMON -DSTRCASECMP] -o mwhod mwhod.c MUD rwho daemon. This operates with 2 sockets open, one for datagrams containing update information for the database, the other a stream connection for users to connect to and download information about what MUDs are up and who is on. mwhods can share their databases between eachother by being defined as PEERs in the configuration file. The config file specifies a list of MUDs or servers that we trust and will accept data from, along with passwords to use when communicating with them. Information is stored about the number of times a data item has been passed, and the generation count is incremented each time the datum is re-forwarded. After a certain number of generations, data will no longer be forwarded. The server makes a traverse of its database periodically and expires all information that it has not received an update for in a given period of time. This is settable arbitrarily, but should not be too high a value. Thanks to John Kennedy for fork() mods to prevent write drops. */ #include "os.h" #ifdef WIN32 #include "getopt.h" #endif extern char *optarg; /* change this? */ static char connectmsg[] = "Connected to Mud RWHO server...\n"; //FILE *logfile = stderr; FILE *logfile = NULL; /* hear nothing from a MUD for more than this, expire it */ #define MUDTTL 800; #define PLYTTL 800; /* name of server, in case we need to propagate */ char *myname = (char *) 0; /* name of default muds database to load */ #define DEFMUDTAB "muds.dat" /* ports to listen at */ #define DGRAMPORT 6888 #define STREAMPORT 6889 /* clean up our tables and propagate them every this many seconds */ #define CLEANUPEVERY 120 int cleantime = CLEANUPEVERY; int proptime = CLEANUPEVERY; /* states of user table entries */ #define U_ALIVE 01 #define U_ZOMBIE 02 #define U_KILL 04 #define PNAMSIZ 32 struct uent { char uid[PNAMSIZ]; /* user id (must be unique) */ char uname[PNAMSIZ]; /* user name (arbitrary text) */ int state; /* user state */ time_t logon; /* logon time at remote */ time_t ttl; /* time to live of logon entry */ time_t upd; /* last update on ttl */ int gen; /* number of generations of prop */ struct uent *next; }; /* states of mudtable entries */ #define MUD_UP 001 #define MUD_PEER 002 #define MUD_GUEST 004 #define MUD_WIZ 010 struct mudent { struct in_addr maddr; /* remote address */ short mport; /* remote port */ char *mapw; /* remote password */ char *mnam; /* remote MUD name */ char *txt; /* arbitrary text about MUD */ time_t up; /* MUD's uptime */ int ucnt; /* active (?) users */ time_t ttl; /* time to live for MUD entry */ time_t upd; /* last update on ttl */ int flgs; /* flags */ int gen; /* generation to PROPAGATE if peer */ struct uent *usrs; struct mudent *next; }; struct mudent *mt; int havepeers = 0; extern struct mudent *getmudent(char *mud, int cflg); extern void writepidfile(char *f); extern void process_stream(SOCKET fd); extern void process_dgram(struct in_addr *who, char *buf); extern void mud_free_ulist(struct mudent *m); extern void mud_add_user(int ac, char *av[]); extern void mud_zap_user(int ac, char *av[]); extern void mud_add_entry(int ac, char *av[], int flg); extern void mud_zap_entry(int ac, char *av[]); extern void mud_def_newmud(char *buf); extern void multicast_tables(SOCKET fd); extern int loadmudtab(char *fil); static void loaddefmudtab(void); static void cleandeath(void); static int stream_mudent(SOCKET fd, struct mudent *m, int *ucnt); static void lookup_who(SOCKET fd, char *w); static void lookup_mud(SOCKET fd, char *mud); static void childclean(void); static void clean_tables (time_t now); time_t serverbooted; int dbgflg = 0; int logflg = 0; char *mudtab = DEFMUDTAB; SOCKET dgramfd; SOCKET streamfd; /* load the default MUD table (or reload it) */ static void loaddefmudtab () { if (dbgflg) printf ("reloading mud table\n"); (void) loadmudtab (mudtab); } static void cleandeath () { (void) shutdown (dgramfd, 2); (void) shutdown (streamfd, 2); if (dbgflg) printf ("shutdown\n"); WIN32CLEANUP exit (0); } int main (int ac, char **av) { struct sockaddr_in addr; struct timeval timo; fd_set redy; fd_set xcpt; time_t lastclean = (time_t) 0; time_t lastprop = (time_t) 0; time_t now; char rbuf[512]; int red; int alen; unsigned mydgport = DGRAMPORT; unsigned mystport = STREAMPORT; time (&serverbooted); while ((alen = getopt (ac, av, "S:P:c:p:f:n:dlL:DF:")) != EOF) { switch (alen) { case 'S': mystport = atoi (optarg); break; case 'P': mydgport = atoi (optarg); break; case 'F': writepidfile (optarg); break; case 'f': mudtab = optarg; break; case 'n': myname = optarg; break; case 'c': cleantime = atoi (optarg); break; case 'p': proptime = atoi (optarg); break; case 'd': dbgflg++; break; case 'l': logflg++; break; case 'L': logfile = fopen (optarg, "wb"); if (logfile == (FILE *) 0) perror (optarg); break; case 'D': #ifdef DAEMON (void) signal (SIGTERM, SIG_IGN); (void) signal (SIGHUP, SIG_IGN); (void) signal (SIGCHLD, SIG_IGN); (void) signal (SIGALRM, SIG_IGN); if (fork ()) exit (0); #ifdef SETSID (void) setsid (); #endif #else fprintf (stderr, "cannot daemonize\n"); #endif break; default: fprintf (stderr, "usage: %s [-d] -f mudlist -n name\n", av[0]); exit (1); } } if (loadmudtab (mudtab)) exit (1); (void) signal (SIGFPE, loaddefmudtab); (void) signal (SIGINT, cleandeath); #ifndef WIN32 (void) signal (SIGPIPE, SIG_IGN); (void) signal (SIGCHLD, childclean); #endif if (havepeers && myname == (char *) 0) { fprintf (stderr, "cannot have server name unset and propagate\n"); exit (1); } WIN32STARTUP /* set up datagram service */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons (mydgport); if ((dgramfd = socket (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { perror ("socket"); WIN32CLEANUP exit (1); } if (bind (dgramfd, (struct sockaddr *) &addr, sizeof (addr))) { perror ("bind"); WIN32CLEANUP exit (1); } alen = 1; setsockopt (dgramfd, SOL_SOCKET, SO_REUSEADDR, &alen, sizeof (alen)); /* set up stream service */ addr.sin_port = htons (mystport); if ((streamfd = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { perror ("socket"); WIN32CLEANUP exit (1); } if (bind (streamfd, (struct sockaddr *) &addr, sizeof (addr))) { perror ("bind"); WIN32CLEANUP exit (1); } alen = 1; setsockopt (streamfd, SOL_SOCKET, SO_REUSEADDR, &alen, sizeof (alen)); if (listen (streamfd, 5) == SOCKET_ERROR) { perror ("listen"); WIN32CLEANUP exit (1); } timo.tv_sec = cleantime > proptime ? cleantime : proptime; timo.tv_usec = 0; if (logflg && logfile != (FILE *) 0) fprintf (logfile, "server running: port %d/%d\n", mystport, mydgport); if (dbgflg) printf ("server running: port %d/%d\n", mystport, mydgport); /* main thang */ looptop: /* how often to purge expired entries */ time (&now); if (now - lastclean > cleantime) { clean_tables (now); lastclean = now; if (logfile != (FILE *) 0) fflush (logfile); } /* how often to propagate */ if (now - lastprop > proptime) { if (havepeers) multicast_tables (dgramfd); lastprop = now; } FD_ZERO (&redy); FD_ZERO (&xcpt); FD_SET (streamfd, &redy); FD_SET (streamfd, &xcpt); FD_SET (dgramfd, &redy); FD_SET (dgramfd, &xcpt); if ((red = select (streamfd + 1, &redy, (fd_set *) 0, &xcpt, &timo)) == SOCKET_ERROR) if (GETERROR != EINTR) { perror ("select"); WIN32CLEANUP exit (1); } /* yawn */ if (red <= 0) goto looptop; if (FD_ISSET (dgramfd, &redy)) { alen = sizeof (addr); red = recvfrom (dgramfd, rbuf, sizeof (rbuf), 0, (struct sockaddr *) &addr, &alen); if (red > 0 && red < (int) sizeof (rbuf)) { rbuf[red] = '\0'; process_dgram (&addr.sin_addr, rbuf); } } if (FD_ISSET (streamfd, &redy)) { red = accept (streamfd, (struct sockaddr *) 0, (int *) 0); if (red != INVALID_SOCKET) process_stream (red); } if (FD_ISSET (streamfd, &xcpt) || FD_ISSET (dgramfd, &xcpt)) { if (logfile != (FILE *) 0) fprintf (logfile, "we are betrayed!!\n"); WIN32CLEANUP exit (1); } goto looptop; } /* scan the MUD table */ struct mudent *getmudent (char *mud, int cflg) { struct mudent *ret; for (ret = mt; ret != (struct mudent *) 0; ret = ret->next) { #ifdef STRCASECMP if (ret->mnam != (char *) 0 && ((!cflg && !strcasecmp (ret->mnam, mud)) || (cflg && !strcmp (ret->mnam, mud)))) #else if (ret->mnam != (char *) 0 && !strcmp (ret->mnam, mud)) #endif return (ret); } return ((struct mudent *) 0); } /* zip through our tables and kill anything with an expired time-to-live */ static void clean_tables (time_t now) { struct mudent *m; struct uent *u; struct uent *p; struct uent *n; if (dbgflg) printf ("cleaning deadwood out of tables\n"); for (m = mt; m != (struct mudent *) 0; m = m->next) { if (!(m->flgs & MUD_UP)) continue; /* has the whole MUD entry table expired? */ if (now - m->upd > m->ttl) { if (logflg && logfile != (FILE *) 0) fprintf (logfile, "dead mud %s\n", m->mnam); if (dbgflg) printf ("dead %s: now=%d, lastup=%d, ttl=%d\n", m->mnam, (int) now, (int) m->upd, (int) m->ttl); m->flgs &= ~MUD_UP; mud_free_ulist (m); continue; } /* check for expired logins */ for (p = u = m->usrs; u != (struct uent *) 0; u = n) { n = u->next; if (now - u->upd > u->ttl || (u->state == U_KILL)) { if (logflg && logfile != (FILE *) 0) fprintf (logfile, "dead user %s@%s\n", u->uid, m->mnam); if (dbgflg) printf ("dead user %s@%s\n", u->uid, m->mnam); if (u->state == U_ALIVE) m->ucnt--; if (u == m->usrs) p = m->usrs = n; else p->next = n; free (u); } else { /* Only advance prev if we did NOT nuke */ p = u; } } } } /* propagate out our tables to peer servers, if any */ void multicast_tables (SOCKET fd) { struct sockaddr_in ad; struct uent *u; struct mudent *m; struct mudent *m2; char v[512]; if (dbgflg) printf ("propagating tables\n"); for (m = mt; m != (struct mudent *) 0; m = m->next) { if (!(m->flgs & MUD_PEER)) continue; /* setup address */ ad.sin_port = htons (m->mport); ad.sin_family = AF_INET; bcopy (&m->maddr, &ad.sin_addr, sizeof (ad.sin_addr)); if (dbgflg) printf ("updating peer %s@%s, port %d\n", m->mnam, inet_ntoa (m->maddr), m->mport); for (m2 = mt; m2 != (struct mudent *) 0; m2 = m2->next) { /* EXCLUDE ! */ if (m2 == m || m2->gen >= m->gen || m2->flgs & MUD_PEER || !(m2->flgs & MUD_UP)) continue; if (dbgflg) printf ("update mud %s->%s\n", m2->mnam, m->mnam); /* inform remote of the MUD */ sprintf (v, "M\t%s\t%s\t%s\t%d\t%d\t%s", myname, m->mapw, m2->mnam, (int) m2->up, m2->gen + 1, m2->txt != (char *) 0 ? m2->txt : ""); sendto (fd, v, strlen (v), 0, (struct sockaddr *) &ad, sizeof (ad)); for (u = m2->usrs; u != (struct uent *) 0; u = u->next) { if (u->gen + 1 > m->gen || (u->state == U_KILL)) continue; /* if the user is logged out send a byebye by propagating the zombie record. otherwise send a logged in record. */ if (u->state == U_ZOMBIE) { if (dbgflg) printf ("deluser %s->%s\n", u->uid, m->mnam); sprintf (v, "Z\t%s\t%s\t%s\t%s", myname, m->mapw, m2->mnam, u->uid); } else { if (dbgflg) printf ("send user %s->%s\n", u->uid, m->mnam); sprintf (v, "A\t%s\t%s\t%s\t%s\t%d\t%d\t%s", myname, m->mapw, m2->mnam, u->uid, (int) u->logon, u->gen + 1, u->uname); } sendto (fd, v, strlen (v), 0, (struct sockaddr *) &ad, sizeof (ad)); } } } /* reap zombies - by marking them all as killable */ for (m2 = mt; m2 != (struct mudent *) 0; m2 = m2->next) for (u = m2->usrs; u != (struct uent *) 0; u = u->next) if (u->state == U_ZOMBIE) u->state = U_KILL; } static int stream_mudent (SOCKET fd, struct mudent *m, int *ucnt) { struct tm *tim; struct uent *u; char buf[512]; char ibuf[64]; int bl; if (m->txt == (char *) 0) strcpy (ibuf, m->mnam); else sprintf (ibuf, "%.13s/%.22s", m->mnam, m->txt); /* is the MUD running or what? */ if (!(m->flgs & MUD_UP) && m->up == (time_t) 0) { tim = localtime (&serverbooted); sprintf (buf, "\n%-35.35s not heard from since %d/%d/%d %2.2d:%2.2d\n", ibuf, tim->tm_mon, tim->tm_mday, tim->tm_year, tim->tm_hour, tim->tm_min); } else { tim = localtime (&m->up); sprintf (buf, "\n%-35.35s %-3d user%c %s %d/%d/%d %2.2d:%2.2d\n", ibuf, m->ucnt, m->ucnt == 1 ? ' ' : 's', (m->flgs & MUD_UP) ? "up" : "last up", tim->tm_mon + 1, tim->tm_mday, tim->tm_year, tim->tm_hour, tim->tm_min); } bl = strlen (buf); if (send (fd, buf, bl, 0) != bl) return (1); if (!(m->flgs & MUD_UP)) return (0); if ((u = m->usrs) == (struct uent *) 0 || m->ucnt <= 0) return (0); sprintf (buf, "%-35.35s %-17.17s %s\n", "--name--", "--id--", "--login--"); bl = strlen (buf); if (send (fd, buf, bl, 0) != bl) return (1); while (u != (struct uent *) 0) { if (u->state != U_ALIVE) { u = u->next; continue; } tim = localtime (&u->logon); sprintf (buf, "%-35.35s %-17.17s %2.2d:%2.2d.%2.2d\n", u->uname[0] == '\0' ? u->uid : u->uname, u->uname[0] == '\0' ? "" : u->uid, tim->tm_hour, tim->tm_min, tim->tm_sec); bl = strlen (buf); if (send (fd, buf, bl, 0) != bl) return (1); u = u->next; (*ucnt)++; } return (0); } static void lookup_who (SOCKET fd, char *w) { struct mudent *m; struct uent *u; struct tm *tim; char buf[512]; char ibuf[64]; int bl; int got = 0; static char nomsg[] = "nobody by that name is logged in\n"; for (m = mt; m != (struct mudent *) 0; m = m->next) for (u = m->usrs; u != (struct uent *) 0; u = u->next) { #ifdef STRCASECMP if (u->state == U_ALIVE && (!strcasecmp (u->uname, w) || !strcasecmp (u->uid, w))) { #else if (u->state == U_ALIVE && (!strcmp (u->uname, w) || !strcmp (u->uid, w))) { #endif tim = localtime (&u->logon); sprintf (ibuf, "%.10s/%.35s", m->mnam, u->uname[0] == '\0' ? u->uid : u->uname); sprintf (buf, "%-35.35s %-17.17s %2.2d:%2.2d.%2.2d\n", ibuf, u->uname[0] == '\0' ? "" : u->uid, tim->tm_hour, tim->tm_min, tim->tm_sec); bl = strlen (buf); got++; if (send (fd, buf, bl, 0) != bl) return; } } if (!got) (void) send (fd, nomsg, sizeof (nomsg) - 1, 0); } static void lookup_mud (SOCKET fd, char *mud) { struct mudent *m; static char nomud[] = "no MUD by that name is running\n"; int dummy; if ((m = getmudent (mud, 0)) == (struct mudent *) 0) { (void) send (fd, nomud, sizeof (nomud) - 1, 0); return; } (void) stream_mudent (fd, m, &dummy); } /* this is what most users will see, when they connect and ask who is on */ void process_stream (SOCKET fd) { struct mudent *m; fd_set redy; fd_set xcpt; struct timeval timo; int reqst = 0; char rbuf[512]; int red; int mcnt = 0; int ucnt = 0; #ifdef DAEMON if (fork () == 0) { #endif timo.tv_sec = 0; timo.tv_usec = 300; FD_ZERO (&redy); FD_ZERO (&xcpt); FD_SET (fd, &redy); FD_SET (fd, &xcpt); /* see if there's a request packet */ if ((red = select (fd + 1, &redy, (fd_set *) 0, &xcpt, &timo)) > 0) { if ((red = recv (fd, rbuf, sizeof (rbuf),0)) > 0) { char *p; if (!strncmp (rbuf, "mud=", 4) || !strncmp (rbuf, "who=", 4)) reqst = 1; p = rbuf; while (*p != '\0') { if (*p == '\n') { *p = '\0'; break; } p++; } } } if (dbgflg) printf ("start user query\n"); if (send (fd, connectmsg, sizeof (connectmsg) - 1, 0) != (sizeof (connectmsg) - 1)) goto done; if (reqst && !strncmp (rbuf, "mud=", 4)) { if (dbgflg) printf ("lookup_mud %s\n", rbuf); lookup_mud (fd, &rbuf[4]); goto done; } if (reqst && !strncmp (rbuf, "who=", 4)) { if (dbgflg) printf ("lookup_who %s\n", rbuf); lookup_who (fd, &rbuf[4]); goto done; } for (m = mt; m != (struct mudent *) 0; m = m->next) { if (m->flgs & MUD_PEER) continue; if (!(m->flgs & MUD_UP)) { if (dbgflg) printf ("skip down MUD %s\n", m->mnam); continue; } if (stream_mudent (fd, m, &ucnt)) break; if (dbgflg) printf ("sent mudent for MUD %s\n", m->mnam); mcnt++; } sprintf (rbuf, "\n\n --- %d muds, %d players known to this server ---\n", mcnt, ucnt); (void) send (fd, rbuf, strlen (rbuf), 0); done: if (dbgflg) printf ("end user query\n"); #ifdef DAEMON (void) exit (0); } #endif closesocket (fd); } /* process and deal with a datagram from a remote MUD or server */ void process_dgram (struct in_addr *who, char *buf) { struct mudent *m; char *cp; char *av[12]; int ac; /* tokenize at tabs */ av[ac = 0] = cp = buf; while (*cp != '\0') { if (*cp == '\t') { if (ac > 10) return; *cp++ = '\0'; av[++ac] = cp; av[ac + 1] = (char *) 0; } else cp++; } /* minimum req: OP, mudname, password */ if (ac < 3) { if (logflg && logfile != (FILE *) 0) fprintf (logfile, "malformed input: %s\n", inet_ntoa (*who)); if (dbgflg) printf ("malformed input from : %s\n", inet_ntoa (*who)); return; } /* authenticate */ for (m = mt; m != (struct mudent *) 0; m = m->next) { if (m->flgs & MUD_GUEST) continue; if (m->mnam != (char *) 0 && !strcmp (m->mnam, av[1])) { if (bcmp (who, &m->maddr, sizeof (struct in_addr))) { if (logfile != (FILE *) 0) fprintf (logfile, "spoof %s from %s\n", av[1], inet_ntoa (*who)); return; } break; } } /* unknown MUD */ if (m == (struct mudent *) 0) { if (logfile != (FILE *) 0) fprintf (logfile, "probe from %s@%s", av[1], inet_ntoa (*who)); if (logfile != (FILE *) 0 && ac > 2) fprintf (logfile, " (pass=%s)", av[2]); if (logfile != (FILE *) 0) fprintf (logfile, "\n"); return; } /* bogey */ if (strcmp (m->mapw, av[2])) { if (logfile != (FILE *) 0) fprintf (logfile, "bad passwd %s from %s@%s\n", av[2], av[1], inet_ntoa (*who)); return; } /* nice to hear from you again - update remote's ttl */ time (&m->upd); /* parameters passed are: operation, mudname, passwd since we have already used them by this point, we can now shift stuff around when we pass it to the actual operands themselves. */ switch (*av[0]) { /* remote MUD declares it is alive and booted */ case 'U': mud_add_entry (ac - 2, &av[3], 1); break; /* remote MUD existence update only (nondestructive) */ case 'M': mud_add_entry (ac - 2, &av[3], 0); break; /* remote MUD declares it is down and out */ case 'D': mud_zap_entry (ac - 2, &av[3]); break; /* add a new logged in user */ case 'A': mud_add_user (ac - 2, &av[3]); break; /* zap an old logged in user */ case 'Z': mud_zap_user (ac - 2, &av[3]); break; default: return; } } /* read the MUD table of known servers and trusted MUDs */ int loadmudtab (char *fil) { FILE *inf; char buf[BUFSIZ]; if ((inf = fopen (fil, "rb")) == (FILE *) 0) { perror (fil); return (1); } while (fgets (buf, sizeof (buf), inf) != (char *) 0) { if (*buf == '#' || *buf == '\n') continue; mud_def_newmud (buf); } fclose (inf); return (0); } /* wipe the user list for a MUD */ void mud_free_ulist (struct mudent *m) { struct uent *u; struct uent *n; if (dbgflg) printf ("clearing user list for %s\n", m->mnam); u = m->usrs; while (u != (struct uent *) 0) { n = u->next; if (dbgflg) printf ("clearing user %s@%s\n", u->uid, m->mnam); free (u); u = n; } m->usrs = (struct uent *) 0; m->ucnt = 0; } /* add a user entry to a defined MUD if we can parameters are: mudname username login-time propagation-generation */ void mud_add_user (int ac, char *av[]) { struct uent *u; struct uent *p1; struct uent *p2; struct mudent *m; int tgen; m = getmudent (av[0], 1); if (ac < 4 || m == (struct mudent *) 0) { if (dbgflg && ac > 3 && m == (struct mudent *) 0) printf ("unknown mud %s\n", av[0]); if (dbgflg && ac < 4) printf ("add user: bad arg count %d\n", ac); return; } if ((tgen = atoi (av[3])) > m->gen) { if (dbgflg) printf ("ignore gen %d %s@%s\n", tgen, av[1], m->mnam); return; } u = m->usrs; while (u != (struct uent *) 0) { if (!strcmp (u->uid, av[1])) { time (&u->upd); u->gen = tgen; if (u->state != U_ALIVE) { u->state = U_ALIVE; if (dbgflg) printf ("resurrect %s@%s\n", av[1], m->mnam); m->ucnt++; } else { if (dbgflg) printf ("refresh %s@%s\n", av[1], m->mnam); } /* name change? */ if (ac == 5 && strcmp (u->uname, av[4])) strncpy (u->uname, av[4], PNAMSIZ - 2); u->uname[PNAMSIZ - 1] = '\0'; return; } u = u->next; } u = (struct uent *) malloc (sizeof (struct uent)); if (u == (struct uent *) 0) return; u->ttl = PLYTTL; u->upd = m->upd; u->state = U_ALIVE; u->logon = (time_t) atoi (av[2]); u->gen = tgen; strncpy (u->uid, av[1], PNAMSIZ - 2); u->uid[PNAMSIZ - 1] = '\0'; u->uname[0] = '\0'; if (ac == 5) strncpy (u->uname, av[4], PNAMSIZ - 2); else strncpy (u->uname, av[1], PNAMSIZ - 2); u->uname[PNAMSIZ - 1] = '\0'; m->ucnt++; if (logflg && logfile != (FILE *) 0) fprintf (logfile, "new entry %s@%s\n", av[1], m->mnam); if (dbgflg) printf ("new entry %s@%s\n", av[1], m->mnam); p2 = (struct uent *) 0; for (p1 = m->usrs; p1 != (struct uent *) 0; p1 = p1->next) { #ifdef STRCASECMP if (strcasecmp (p1->uname, u->uname) >= 0) #else if (strcmp (p1->uname, u->uname) >= 0) #endif break; p2 = p1; } if (p2 == (struct uent *) 0) { u->next = m->usrs; m->usrs = u; } else { u->next = p2->next; p2->next = u; } } /* zap a user parameters are: mudname username */ void mud_zap_user (int ac, char *av[]) { struct mudent *m; struct uent *u; m = getmudent (av[0], 1); if (ac != 2 || m == (struct mudent *) 0) { if (dbgflg && ac == 2 && m == (struct mudent *) 0) printf ("unknown mud %s\n", av[0]); if (dbgflg && ac != 2) printf ("zap user: bad arg count %d\n", ac); return; } u = m->usrs; while (u != (struct uent *) 0) { if (u->state == U_ALIVE && !strcmp (u->uid, av[1])) { m->ucnt--; u->state = U_ZOMBIE; if (logflg && logfile != (FILE *) 0) fprintf (logfile, "logout %s@%s\n", av[1], m->mnam); if (dbgflg) printf ("logout %s@%s\n", av[1], m->mnam); } u = u->next; } } /* define a new entry for a MUD, or mark one as up and well parameters are: mudname uptime propagation-generation moretext */ void mud_add_entry (int ac, char *av[], int flg) { struct mudent *m; int tgen; m = getmudent (av[0], 1); if (ac < 3 || m == (struct mudent *) 0) { struct mudent *m1; struct mudent *m2; if (ac < 3) { if (dbgflg) printf ("add entry: bad arg count %d\n", ac); return; } m = (struct mudent *) malloc (sizeof (struct mudent)); if (m == (struct mudent *) 0) { perror ("malloc"); return; } m->mapw = (char *) 0; m->mnam = malloc ((unsigned) (strlen (av[0]) + 1)); if (m->mnam == (char *) 0) { perror ("malloc"); return; } strcpy (m->mnam, av[0]); m->txt = (char *) 0; m->ucnt = 0; m->flgs = MUD_GUEST; m->usrs = (struct uent *) 0; m->gen = atoi (av[2]); m2 = (struct mudent *) 0; for (m1 = mt; m1 != (struct mudent *) 0; m1 = m1->next) { #ifdef STRCASECMP if (strcasecmp (m1->mnam, m->mnam) >= 0) #else if (strcmp (m1->mnam, m->mnam) >= 0) #endif break; m2 = m1; } if (m2 == (struct mudent *) 0) { m->next = mt; mt = m; } else { m->next = m2->next; m2->next = m; } if (logflg && logfile != (FILE *) 0) fprintf (logfile, "added mud table entry %s\n", m->mnam); if (dbgflg) printf ("added mud table entry %s\n", m->mnam); } else { /* do not accept info that is too old */ if ((tgen = atoi (av[2])) > m->gen) { if (dbgflg) printf ("ignored gen %d uptime %s\n", tgen, m->mnam); return; } } if (ac > 3) { /* replace name ? */ if (m->txt != (char *) 0 && strcmp (m->txt, av[3])) { free (m->txt); m->txt = (char *) 0; } if (m->txt == (char *) 0) { m->txt = malloc ((unsigned) (strlen (av[3]) + 1)); if (m->txt == (char *) 0) { perror ("malloc"); return; } strcpy (m->txt, av[3]); } } m->flgs |= MUD_UP; m->ttl = MUDTTL; time (&m->upd); m->up = (time_t) atoi (av[1]); /* reset */ if (flg) mud_free_ulist (m); if (dbgflg) printf ("mark mud %s up\n", m->mnam); } /* mark an old entry for a MUD as deleted/down parameters are: mudname */ void mud_zap_entry (int ac, char *av[]) { struct mudent *m; m = getmudent (av[0], 1); if (ac != 1 || m == (struct mudent *) 0) { if (dbgflg && ac == 1 && m == (struct mudent *) 0) printf ("unknown mud %s\n", av[0]); if (dbgflg && ac != 1) printf ("zap entry: bad arg count %d\n", ac); return; } m->flgs &= ~MUD_UP; if (logflg && logfile != (FILE *) 0) fprintf (logfile, "mark mud %s down\n", m->mnam); if (dbgflg) fprintf (logfile, "zapentry mark mud %s down\n", m->mnam); mud_free_ulist (m); } void mud_def_newmud (char *buf) { struct mudent *mudp; struct hostent *hp; struct in_addr ad; int found = 0; short tmport; int tmpflg = 0; int tmpgen = 0; char *cp; char *xp; char *ho; char *nm; char *pw; cp = buf; /* scan out flags */ while (*cp != '\0' && !isspace (*cp)) { switch (*cp) { case 'C': /* no-op */ break; case 'W': /* disused */ break; case 'P': tmpflg |= MUD_PEER; havepeers++; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': tmpgen = *cp - '0'; break; default: fprintf (stderr, "unknown flag %c\n", *cp); return; } cp++; } /* skip space */ while (*cp != '\0' && isspace (*cp)) cp++; /* scan out hostname */ ho = cp; while (*cp != '\0' && !isspace (*cp)) cp++; if (*cp == '\0') { fprintf (stderr, "malformed mud entry: %s\n", buf); return; } *cp++ = '\0'; xp = ho; while (*xp != '\0' && (*xp == '.' || isdigit (*xp))) xp++; /* not all digits or dots */ if (*xp != '\0') { #ifndef NO_HUGE_RESOLVER_CODE if ((hp = gethostbyname (ho)) == (struct hostent *) 0) { fprintf (stderr, "unknown host %s\n", ho); return; } bcopy (hp->h_addr, &ad, hp->h_length); #else fprintf (stderr, "must use hardcoded IP octets\n"); #endif } else { #ifdef WIN32 unsigned long f; #else struct in_addr f; #endif #ifdef WIN32 if ((f = inet_addr (ho)) == INADDR_NONE) { #else if (inet_aton (ho, &f) == 0) { #endif fprintf (stderr, "cannot interpret %s\n", ho); return; } bcopy (&f, &ad, sizeof (f)); } /* skip space */ while (*cp != '\0' && isspace (*cp)) cp++; /* scan out port # */ xp = cp; while (*cp != '\0' && !isspace (*cp)) cp++; if (*cp == '\0') { fprintf (stderr, "missing mud port: %s\n", ho); return; } *cp++ = '\0'; tmport = atoi (xp); /* skip out spaces */ while (isspace (*cp)) cp++; /* get name */ nm = cp; while (*cp != '\0' && !isspace (*cp)) cp++; if (*cp == '\0' || *nm == '\0') { fprintf (stderr, "missing mud name: %s\n", ho); return; } *cp++ = '\0'; /* skip out spaces */ while (isspace (*cp)) cp++; /* get password */ pw = cp; while (*cp != '\0' && !isspace (*cp)) cp++; if (*cp == '\0' || *pw == '\0') { fprintf (stderr, "missing mud password: %s\n", nm); return; } *cp++ = '\0'; /* if new entry */ if ((mudp = getmudent (nm, 0)) == (struct mudent *) 0) { mudp = (struct mudent *) malloc (sizeof (struct mudent)); if (mudp == (struct mudent *) 0) { perror ("malloc"); return; } if (dbgflg) printf ("alloc new table entry for %s\n", nm); mudp->usrs = (struct uent *) 0; mudp->ucnt = 0; } else { if (dbgflg) printf ("clearing mud table entry %s\n", mudp->mnam); found++; free (mudp->mapw); free (mudp->mnam); if (mudp->txt != (char *) 0) free (mudp->txt); } /* misc text */ while (isspace (*cp)) cp++; mudp->mapw = malloc ((unsigned) (strlen (pw) + 1)); mudp->mnam = malloc ((unsigned) (strlen (nm) + 1)); if (mudp->mapw == (char *) 0 || mudp->mnam == (char *) 0) { perror ("malloc"); return; } strcpy (mudp->mapw, pw); strcpy (mudp->mnam, nm); mudp->txt = (char *) 0; if (*cp != '\0') { char *jnkp = cp; /* lose the newline */ while (*jnkp != '\0' && *jnkp != '\n') jnkp++; if (*jnkp == '\n') *jnkp = '\0'; /* save the text */ mudp->txt = malloc ((unsigned) (strlen (cp) + 1)); if (mudp->txt == (char *) 0) { perror ("malloc"); return; } strcpy (mudp->txt, cp); } mudp->up = (time_t) 0; mudp->flgs = tmpflg; mudp->gen = tmpgen; mudp->mport = tmport; bcopy (&ad, &mudp->maddr, sizeof (ad)); if (!found) { struct mudent *m2; struct mudent *m1; m2 = (struct mudent *) 0; for (m1 = mt; m1 != (struct mudent *) 0; m1 = m1->next) { #ifdef STRCASECMP if (strcasecmp (m1->mnam, mudp->mnam) >= 0) #else if (strcmp (m1->mnam, mudp->mnam) >= 0) #endif break; m2 = m1; } if (m2 == (struct mudent *) 0) { mudp->next = mt; mt = mudp; } else { mudp->next = m2->next; m2->next = mudp; } if (dbgflg) printf ("added mud table entry %s\n", mudp->mnam); } } void writepidfile (char *f) { FILE *pf; if ((pf = fopen (f, "wb")) != (FILE *) 0) { fprintf (pf, "%d\n", getpid ()); fclose (pf); } } static void childclean (void) { (void) wait ((int *) NULL); }