#define NOTIFY_TRACE /* This software is Copyright 1989, 1990, 1992, 1993 by various individuals. Please see the accompanying file COPYRIGHT for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* extern XXX */ char **argvec; #define WRITEV_MAXCNT 16 /* this should be in ... */ #include "db.h" #include "defs.h" #include "ctype.h" #include "match.h" #include "prims.h" #include "utils.h" #include "config.h" #include "params.h" #include "random.h" #include "externs.h" #include "strings.h" #include "property.h" #include "hostname.h" #include "username.h" #include "interface.h" #include "unused-arg.h" #define TN_IAC 255 #define TN_DONT 254 #define TN_DO 253 #define TN_WONT 252 #define TN_WILL 251 #define TN_SB 250 #define TN_SE 240 #ifdef MUD_ID #include #endif #if defined(HOSTNAMES) && !defined(SYNC_HOSTNAMES) #define ASYNC_HOSTNAMES #endif #if !defined(HOSTNAMES) #undef SYNC_HOSTNAMES #undef ASYNC_HOSTNAMES #endif #if defined(USERNAMES) && !defined(SYNC_USERNAMES) #define ASYNC_USERNAMES #endif #if !defined(USERNAMES) #undef SYNC_USERNAMES #undef ASYNC_USERNAMES #endif static void createpidfile(void); static void serverisalive(int); void do_setuid(void); extern int alarm_block, alarm_triggered; extern void time_keeper(void); static int may_announce_status = 0; static int no_output_flushed = 0; /* for debugger patching */ static char *infile = 0; static char *dumpfile = 0; static char *portstr = 0; static char *stderrfile = 0; static int liststrings = 0; #ifdef CMDRING_SIZE static char cmdring_buf[CMDRING_SIZE]; static unsigned int cmdring_head; static unsigned int cmdring_size; #endif #ifdef __linux__ /* Goddam Linux just _has_ to be incompatible - has no sa_len or sin_len. */ #define NO_SOCKADDR_LEN #endif /* * next two subroutines grabbed from mjr's Ubermud * * Put here by WOZ, because he thought it was a * Pretty Neat Trick (tm) and liked being able to see if the server * was still up w/o having to log in. */ /* this is a bizarre little hack. if the server catches a SIGUSR2 ,it will attempt to unlink the file. this is so that a program can test the aliveness of the server, by creating the file, signalling the server, and seeing if the file is still there. the choice to remove the file rather than to create it is because of the possibility of there being a dearth of file descriptors. it does not take an fd to remove a file. */ static void serverisalive(UNUSED_ARG(int sig)) { unlink("server_lives"); log_status("PING: caught."); signal(SIGUSR2, serverisalive); } /* create a file in the server directory with the current process id in it. this is also generally useful, costs nothing, etc. */ /* static */ void createpidfile() { FILE *ff; ff = fopen("server_pid","w"); if (ff == 0) { log_status("PID: cannot open pid file."); return; } fprintf(ff,"%d\n",getpid()); fclose(ff); } #ifdef NOLOGINS /* hack to allow only wizards to log in if NOLOGIN_FILE exists in GAMEDIR, spitting the contents of the NOLOGIN_FILE to non wizards who attempt to connect */ static int no_login (struct descriptor_data *d, dbref player) { int handle; char buf[BUFFER_LEN]; FILE *f; if (Wizard(player)) return(0); handle = open(NOLOGIN_FILE,O_RDONLY,0); if (handle >= 0) { f = fdopen(handle,"r"); if (f) { while (fgets(&buf[0],sizeof(buf),f)) { int n; n = strlen(&buf[0]); if (n && (buf[n-1] == '\0')) n --; queue_write(d,&buf[0],n); queue_write(d,"\r\n",2); } fclose(f); } else { close(handle); } return(1); } else { return(0); } } #endif /*NOLOGINS*/ int shutdown_flag = 0; time_t time_started; extern time_t last_checkpoint; #define CONNECT_FAIL \ "Either that player does not exist, or has a different password.\r\n" #ifndef REGISTRATION #define CREATE_FAIL \ "Either there is already a player with that name, \ or that name is illegal.\r\n" #endif /* REGISTRATION */ #define FLUSHED_MESSAGE "\r\n" #define SHUTDOWN_MESSAGE "\r\nGoing down - Bye\r\n" #define CRASHING_MESSAGE "\r\nGoing down in flames - Bye\r\n" typedef struct listensocket LISTENSOCKET; struct listensocket { int s; struct sockaddr *listen_sa; #ifdef NO_SOCKADDR_LEN int listen_sa_len; #else #define listen_sa_len listen_sa->sa_len #endif int nautocmds; char **autocmds; } ; static LISTENSOCKET **sockets; static int nsockets; static int maxdesc; int big_fat_descripto_lock = 0; const char *shutdown_message = SHUTDOWN_MESSAGE; const char *newcon_warn = 0; struct descriptor_data *descriptor_list = 0; void process_commands(void); void shovechars(void); void make_nonblocking(int); void freeqs(struct descriptor_data *); void welcome_user(struct descriptor_data *); void con_connect(struct descriptor_data *, const char *, const char *); void con_create(struct descriptor_data *, const char *, const char *); void close_sockets(void); int boot_off(dbref); void dump_to_desc(void *, const char *); void dump_to_player(void *, const char *); void dump_descriptors(dbref, const char *); void set_signals(void); struct descriptor_data *new_connection(int); void parse_connect (const char *, char *, char *, char *); void set_userstring (char **, const char *); int do_command (struct descriptor_data *, char *); char *strsave (const char *); int process_input(struct descriptor_data *); void bailout(int); void announce_connect(dbref); void announce_disconnect(dbref); #ifdef BUILTIN_WHO void dump_users(dbref, const char *, void (*)(void *, const char *), void *); #endif #define MALLOC(result, type, number) \ if (!((result) = (type *) malloc ((number) * sizeof (type)))) \ panic("Out of memory"); \ static void sigshutdown(int sig) { log_status("SHUTDOWN: on signal %d",sig); shutdown_flag = 1; } static void cmdring_init(void) { #ifdef CMDRING_SIZE bzero(&cmdring_buf[0],sizeof(cmdring_buf)); cmdring_head = 0; cmdring_size = 0; #endif } #ifdef CMDRING_SIZE static void cmdring_stash_str(const char *s) { unsigned int t; t = strlen(s); if (cmdring_head+t > CMDRING_SIZE) { bcopy(s,&cmdring_buf[cmdring_head],CMDRING_SIZE-cmdring_head); bcopy(s+CMDRING_SIZE-cmdring_head,&cmdring_buf[0],t-(CMDRING_SIZE-cmdring_head)); } else { bcopy(s,&cmdring_buf[cmdring_head],t); } cmdring_head = (cmdring_head + t) % CMDRING_SIZE; cmdring_size += t; if (cmdring_size > CMDRING_SIZE) cmdring_size = CMDRING_SIZE; } #endif void cmdring_dump(int fd) { #ifdef CMDRING_SIZE unsigned int t; t = (cmdring_head + CMDRING_SIZE - cmdring_size) % CMDRING_SIZE; if (t >= cmdring_head) { write(fd,&cmdring_buf[t],CMDRING_SIZE-t); write(fd,&cmdring_buf[0],cmdring_head); } else { write(fd,&cmdring_buf[t],cmdring_size); } #endif } #ifdef CMDRING_SIZE static void cmdring_stash(struct descriptor_data *d, const char *command) { char tmp[64]; if (d->connected) { sprintf(&tmp[0],"%d (%d in %d)",d->descriptor,(int)d->player,(int)DBFETCH(d->player)->location); } else { sprintf(&tmp[0],"%d (unconnected)",d->descriptor); } cmdring_stash_str(&tmp[0]); cmdring_stash_str(command); cmdring_stash_str("\n"); } #endif #ifdef LOCKOUT static int userhostmatch(const char *u, const char *h, const char *l) { #ifdef USERNAMES int ulen; int hlen; int llen; if (!strcmp(h,l)) return(1); ulen = strlen(u); hlen = strlen(h); llen = strlen(l); if ( (llen == hlen+2) && (l[0] == '*') && (l[1] == '@') && !bcmp(l+2,h,hlen) ) return(1); if ( (llen == ulen+2) && !bcmp(l,u,ulen) && (l[ulen] == '@') && (l[ulen+1] == '*') ) return(1); if ( (llen == ulen+1+hlen) && !bcmp(l,u,ulen) && (l[ulen] == '@') && !bcmp(l+ulen+1,h,hlen) ) return(1); return(0); #else u=u; return(!strcmp(h,l)); #endif } #endif static int spit_file_d(const char *file, struct descriptor_data *d) { FILE *f; char buf[BUFFER_LEN]; f = fopen(file,"r"); if (f) { while (fgets(buf,sizeof(buf),f)) { buf[strlen(buf)-1] = '\0'; queue_string(d,buf); queue_write(d,"\r\n",2); } process_output(d); fclose(f); return(0); } else { return(1); } } #ifdef LOCKOUT static int forbidden_site(struct descriptor_data *d) { char buf[512]; char *bp; FILE *fp; int i; int neg; char *at; char *spc; fp = fopen(LOCKOUT_FILE,"r"); if (fp == 0) return(0); while (fgets(&buf[0],sizeof(buf),fp) == &buf[0]) { if (buf[0] == '#') continue; i = strlen(&buf[0]); if ((i > 0) && (buf[i-1] == '\n')) buf[--i] = '\0'; bp = &buf[0]; neg = 0; if (*bp == '!') { bp ++; neg = 1; } at = index(bp,'@'); spc = index(at?at:bp,' '); if (spc) { *spc = '\0'; do spc++; while (*spc == ' '); if (!*spc) spc = 0; } if (userhostmatch(d->username,d->hostname,bp)) { fclose(fp); if (spc && spit_file_d(spc,d)) { log_status("spit_file_d %s for locked out desc %d failed",spc,d->descriptor); } return(!neg); } } fclose(fp); return(0); } #endif static void check_lockout(struct descriptor_data *d) { #ifdef LOCKOUT if (forbidden_site(d)) { log_status("LOCKED OUT: connection from %s@%s on descriptor %d",d->username,d->hostname,d->descriptor); shutdownsock(d); close(d->descriptor); d->descriptor = -1; } #endif } static void check_waits(struct descriptor_data *d) { d->waiting = d->hostnamewait || d->usernamewait; if (d->waiting) return; check_lockout(d); if (! d->booted) welcome_user(d); } #ifdef ASYNC_HOSTNAMES static void process_hostname_output(void) { struct in_addr ia; unsigned char len; char name[256]; struct descriptor_data *d; if ( (Read(hostname_fd,&ia,sizeof(ia)) == 0) && (Read(hostname_fd,&len,1) == 0) && (Read(hostname_fd,&name[0],len) == 0) ) { name[len] = '\0'; for (d=descriptor_list;d;d=d->next) { if ( d->hostnamewait && (((const struct in_addr *)d->hostname)->s_addr == ia.s_addr) ) { const char *new; if (len > 0) { new = &name[0]; log_status("HOSTNAME: %s -> %s for descriptor %d",inet_ntoa(*(const struct in_addr *)d->hostname),new,d->descriptor); } else { new = inet_ntoa(*(const struct in_addr *)d->hostname); log_status("HOSTNAME: %s failed for descriptor %d",new,d->descriptor); } cfree(d->hostname); d->hostname = dup_string(new); d->hostnamewait = 0; check_waits(d); } } } } #endif #ifdef ASYNC_USERNAMES static void process_username_output(void) { struct sockaddr_in sin[2]; unsigned char len; char name[256]; struct descriptor_data *d; if ( (Read(username_fd,&sin[0],sizeof(sin)) == 0) && (Read(username_fd,&len,1) == 0) && (Read(username_fd,&name[0],len) == 0) ) { name[len] = '\0'; #if 0 fprintf(stderr,"searching: %08lx/%hu,%08lx/%hu\n", (unsigned long int) sin[0].sin_addr.s_addr, (unsigned short int) sin[0].sin_port, (unsigned long int) sin[1].sin_addr.s_addr, (unsigned short int) sin[1].sin_port ); #endif for (d=descriptor_list;d;d=d->next) { #if 0 if (d->usernamewait) { fprintf(stderr,"0x%lx: %d, uw=%d %08lx/%hu,%08lx/%hu\n", (unsigned long int)d, d->descriptor, d->usernamewait, (unsigned long int) ((const struct sockaddr_in *)d->username)[0].sin_addr.s_addr, (unsigned short int) ((const struct sockaddr_in *)d->username)[0].sin_port, (unsigned long int) ((const struct sockaddr_in *)d->username)[1].sin_addr.s_addr, (unsigned short int) ((const struct sockaddr_in *)d->username)[1].sin_port ); } else { fprintf(stderr,"0x%lx: %d, uw=%d\n",(unsigned long int)d,d->descriptor,d->usernamewait); } #endif if ( d->usernamewait && (((const struct sockaddr_in *)d->username)[0].sin_addr.s_addr == sin[0].sin_addr.s_addr) && (((const struct sockaddr_in *)d->username)[0].sin_port == sin[0].sin_port) && (((const struct sockaddr_in *)d->username)[1].sin_addr.s_addr == sin[1].sin_addr.s_addr) && (((const struct sockaddr_in *)d->username)[1].sin_port == sin[1].sin_port) ) { cfree(d->username); d->username = dup_string(&name[0]); log_status("USERNAME: %s for descriptor %d",d->username,d->descriptor); d->usernamewait = 0; check_waits(d); } } } } #endif static int handleargs(int ac, char **av) { if (!strcmp(av[1],"-strings") && (ac == 3)) { liststrings = 1; infile = av[2]; return(0); } if (ac != 4) return(1); infile = av[1]; dumpfile = av[2]; portstr = av[3]; return(0); } int main(int, char **); int main(int argc, char **argv) { argvec = argv; /* XXX */ if (handleargs(argc,argv)) { fprintf(stderr,"Usage: %s infile dumpfile port[/port]\n",argvec[0]); fprintf(stderr," or: %s -strings infile\n",argvec[0]); exit(1); } #ifdef MMMALLOC if (! liststrings) { /* FILE *f; f = fopen("logs/malstats","w"); mal_setstatsfile(f); mal_trace(1); mal_sbrkset(65536); */ } #endif if (! liststrings) { #ifdef ASYNC_HOSTNAMES fork_hostname_process(); #endif #ifdef ASYNC_USERNAMES fork_username_process(); #endif } time_started = curtm(); last_checkpoint = curtm(); if (stderrfile && strcmp(stderrfile,"-")) { freopen(stderrfile,"w",stderr); setbuf(stderr,0); } if (! liststrings) log_status("INIT: TinyMUCK %s starting.","version"); init_str(liststrings); if (init_game(infile,dumpfile,liststrings) < 0) { fprintf(stderr,"Couldn't load %s!\n",infile); exit(2); } if (liststrings) { dump_strings(); exit(0); } may_announce_status = 1; set_signals(); #ifdef MUD_ID do_setuid(); #endif cmdring_init(); /* set the file creation mask so no one snarfs the db */ umask(077); /* go do it */ #ifndef AUTODEBUG shovechars(); close_sockets(); #else while(1) sleep(30); #endif dump_database(); free_game(); malloc_leakcheck(); exit(0); } void set_signals(void) { struct sigaction sa; /* we don't care about SIGPIPE, we notice it in select() and write() */ signal(SIGPIPE,SIG_IGN); /* standard termination signals */ signal(SIGINT,sigshutdown); sa.sa_handler = &bailout; sigemptyset(&sa.sa_mask); sa.sa_flags = 0 /*SA_ONSTACK*/; sigaction(SIGTERM,&sa,0); /* catch these because we might as well */ sigaction(SIGQUIT,&sa,0); sigaction(SIGTRAP,&sa,0); sigaction(SIGIOT,&sa,0); #ifdef SIGEMT sigaction(SIGEMT,&sa,0); #endif sigaction(SIGFPE,&sa,0); sigaction(SIGBUS,&sa,0); sigaction(SIGSEGV,&sa,0); sigaction(SIGSYS,&sa,0); sigaction(SIGTERM,&sa,0); sigaction(SIGXCPU,&sa,0); sigaction(SIGXFSZ,&sa,0); sigaction(SIGVTALRM,&sa,0); /* ubermud like checkserver support */ signal(SIGUSR2,&serverisalive); } static int notify_aux(dbref player, const char *msg) { struct descriptor_data *d; int retval; if (player == NOTHING) return(0); retval = 0; for (d=descriptor_list;d;d=d->next) { if (d->connected && (d->player == player)) { queue_string(d,msg); retval ++; } } return(retval); } #ifdef NOTIFY_TRACE dbref last_notify_player[32]; const char *last_notify_msg[32]; unsigned long int last_notify_point; #define SETPOINT(n) do { last_notify_point |= 1 << (n); last_notify_player[(n)] = player; last_notify_msg[(n)] = msg; } while (0) #else #define SETPOINT(n) do { } while (0) #endif int notify(dbref player, const char *msg) { #ifdef NOTIFY_TRACE last_notify_point = 0; #endif SETPOINT(0); if (player == NOTHING) return(0); SETPOINT(1); SETPOINT(2); switch (Typeof(player)) { case TYPE_DAEMON: SETPOINT(3); player = OWNER(player); SETPOINT(4); /* fall through */ case TYPE_PLAYER: SETPOINT(5); if (notify_aux(player,msg)) { int rv; SETPOINT(6); rv = notify_aux(player,"\r\n"); SETPOINT(7); return(rv); } SETPOINT(8); break; } SETPOINT(9); return(0); } int notify_str(dbref player, STR s) { char *t; int rv; t = fetch_str(s); rv = notify(player,t); free(t); return(rv); } int notify_nnl(dbref player, const char *msg) { if (player == NOTHING) return(0); switch (Typeof(player)) { case TYPE_DAEMON: player = OWNER(player); /* fall through */ case TYPE_PLAYER: return(notify_aux(player,msg)); break; } return(0); } static struct timeval timeval_sub(struct timeval now, struct timeval then) { now.tv_sec -= then.tv_sec; now.tv_usec -= then.tv_usec; if (now.tv_usec < 0) { now.tv_usec += 1000000; now.tv_sec--; } return now; } static int msec_diff(struct timeval now, struct timeval then) { return ((now.tv_sec - then.tv_sec) * 1000 + (now.tv_usec - then.tv_usec) / 1000); } static struct timeval msec_add(struct timeval t, int x) { t.tv_sec += x / 1000; t.tv_usec += (x % 1000) * 1000; if (t.tv_usec >= 1000000) { t.tv_sec += t.tv_usec / 1000000; t.tv_usec = t.tv_usec % 1000000; } return t; } static struct timeval update_quotas(struct timeval last, struct timeval current) { int nslices; int cmds_per_time; struct descriptor_data *d; nslices = msec_diff (current, last) / COMMAND_TIME_MSEC; if (nslices > 0) { for (d = descriptor_list; d; d = d -> next) { if (d -> connected) cmds_per_time = ((FLAGS(d->player) & INTERACTIVE) ? COMMAND_BURST_SIZE : COMMANDS_PER_TIME); else cmds_per_time = COMMANDS_PER_TIME; d -> quota += cmds_per_time * nslices; if (d -> quota > COMMAND_BURST_SIZE) d -> quota = COMMAND_BURST_SIZE; } } return msec_add (last, nslices * COMMAND_TIME_MSEC); } void newcon_warn_set(const char *msg) { newcon_warn = msg; } static int accept_a_conn(fd_set *rfds) { struct descriptor_data *d; int i; LISTENSOCKET *s; d = 0; for (i=0;is,rfds)) { d = new_connection(s->s); if (d) { int a; for (a=0;anautocmds;a++) { if (! do_command(d,s->autocmds[a])) { shutdownsock(d); return(1); } } break; } } } if (! d) return(0); if (newcon_warn && !d->booted) { queue_string(d,newcon_warn); queue_write(d,"\r\n",2); queue_string(d,"Anything you type will be handled once this is finished.\r\n"); queue_string(d,"Please be patient...\r\n"); } return(1); } void newcon_check(void) { fd_set fds; struct timeval tv; struct descriptor_data *d; int i; if (! newcon_warn) return; do { FD_ZERO(&fds); for (i=0;is,&fds); tv.tv_sec = 0; tv.tv_usec = 0; if (select(FD_SETSIZE,&fds,0,0,&tv) < 0) return; } while (accept_a_conn(&fds)); FD_ZERO(&fds); for (d=descriptor_list;d;d=d->next) { if (d->booted) continue; if (d->output.head) FD_SET(d->descriptor,&fds); } tv.tv_sec = 0; tv.tv_usec = 0; if (select(FD_SETSIZE,0,&fds,0,&tv) < 0) return; for (d=descriptor_list;d;d=d->next) { if (d->booted) continue; if (d->output.head && FD_ISSET(d->descriptor,&fds)) { process_output(d); } } } static void add_socket(LISTENSOCKET *s) { int sock; int opt; sock = socket(s->listen_sa->sa_family,SOCK_STREAM,0); if (sock < 0) { fprintf(stderr,"%s: socket: %s\n",argvec[0],strerror(errno)); exit(1); } opt = 1; if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void *)&opt,sizeof(opt)) < 0) { fprintf(stderr,"%s: setsockopt SO_REUSEADDR: %s\n",argvec[0],strerror(errno)); exit(1); } if (bind(sock,(void *)s->listen_sa,s->listen_sa_len) < 0) { int err; err = errno; fprintf(stderr,"%s: bind: %s\n",argvec[0],strerror(err)); log_status("bind: %s",strerror(err)); exit(1); } listen(sock,10); s->s = sock; sockets = realloc(sockets,(nsockets+1)*sizeof(LISTENSOCKET)); sockets[nsockets++] = s; } static void add_autocmd(LISTENSOCKET *s, const char *cmd) { s->autocmds = realloc(s->autocmds,(s->nautocmds+1)*sizeof(char *)); s->autocmds[s->nautocmds++] = strdup(cmd); } static void setup_sockets_compat(void) { char *psp; char *psp2; int telnet_y; telnet_y = 0; psp = portstr; while (*psp) { long int port; struct sockaddr_in *sin; LISTENSOCKET *s; switch (*psp) { case ',': psp ++; continue; break; case '/': psp ++; telnet_y = 1; continue; break; } port = strtol(psp,&psp2,0); if (psp2 == psp) { fprintf(stderr,"%s: invalid port string %s\n",argvec[0],portstr); exit(1); } if ((port < 1) || (port > 65535)) { fprintf(stderr,"%s: invalid port number %ld\n",argvec[0],port); exit(1); } psp = psp2; sin = malloc(sizeof(struct sockaddr_in)); bzero(sin,sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; sin->sin_port = htons(port); s = malloc(sizeof(LISTENSOCKET)); s->listen_sa = (void *) sin; s->nautocmds = 0; s->autocmds = 0; #ifdef NO_SOCKADDR_LEN s->listen_sa_len = sizeof(*sin); #else sin->sin_len = sizeof(*sin); #endif if (telnet_y) add_autocmd(s,"TELNET Y"); add_socket(s); } } static void setup_sockets_config(void) { FILE *f; char line[512]; LISTENSOCKET *s; char *lp; char *cp; int l; int lno; auto void err(const char *, ...) __attribute__((__format__(__printf__,1,2),__noreturn__)); auto void err(const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: \"%s\", line %d: ",argvec[0],portstr,lno); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); exit(1); } auto void warn(const char *, ...) __attribute__((__format__(__printf__,1,2))); auto void warn(const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: \"%s\", line %d: ",argvec[0],portstr,lno); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); } f = fopen(portstr,"r"); s = 0; lno = 0; while (fgets(&line[0],sizeof(line),f)) { lno ++; l = strlen(&line[0]); if ((l > 0) && (line[l-1] == '\n')) line[l-1] = '\0'; lp = &line[0]; while (*lp && Cisspace(*lp)) lp++; if (*lp == '#') continue; if (! *lp) { s = 0; continue; } cp = lp; do lp++; while (*lp && !Cisspace(*lp)); l = lp - cp; if ((l == 4) && !bcmp(cp,"port",4)) { long int port; struct sockaddr_in *sin; if (s) err("`port' command not at beginning of stanza"); port = strtol(lp,&cp,0); if (cp == lp) err("missing/unrecognizable port number"); while (*cp && Cisspace(*cp)) cp ++; if (*cp && (*cp != '#')) err("junk after port number"); if ((port < 1) || (port > 65535)) err("port %ld is out of range",port); sin = malloc(sizeof(struct sockaddr_in)); bzero(sin,sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; sin->sin_port = htons(port); s = malloc(sizeof(LISTENSOCKET)); s->listen_sa = (void *) sin; s->nautocmds = 0; s->autocmds = 0; #ifdef NO_SOCKADDR_LEN s->listen_sa_len = sizeof(*sin); #else sin->sin_len = sizeof(*sin); #endif add_socket(s); } else if ((l == 6) && !bcmp(cp,"listen",6)) { char *abeg; char *aend; char t; long int port; struct sockaddr_in *sin; /* XXX should use getnameinfo here, to support IPv6 and such! */ if (s) err("`listen' command not at beginning of stanza"); sin = malloc(sizeof(*sin)); bzero(sin,sizeof(*sin)); sin->sin_family = AF_INET; for (;*lp&&Cisspace(*lp);lp++) ; abeg = lp; for (;*lp&&!Cisspace(*lp);lp++) ; aend = lp; t = *aend; *aend = '\0'; if (! inet_aton(abeg,&sin->sin_addr)) { struct hostent *hp; hp = gethostbyname(abeg); if (hp == 0) err("hostname `%s' unknown",abeg); if (! hp->h_addr_list[0]) err("hostname `%s' has no addresses",abeg); if (hp->h_addrtype != AF_INET) err("hostname `%s' has non-INET address",abeg); if (hp->h_length != sizeof(struct in_addr)) err("hostname `%s' address length wrong",abeg); if (hp->h_addr_list[1]) warn("hostname `%s' has multiple addresses, using %s\n",abeg,inet_ntoa(*(struct in_addr *)hp->h_addr_list[0])); sin->sin_addr = *(struct in_addr *)hp->h_addr_list[0]; } *aend = t; port = strtol(aend,&cp,0); if (cp == lp) err("missing/unrecognizable port number"); while (*cp && Cisspace(*cp)) cp ++; if (*cp && (*cp != '#')) err("junk after port number"); if ((port < 1) || (port > 65535)) err("port %ld is out of range",port); sin->sin_port = htons(port); s = malloc(sizeof(LISTENSOCKET)); s->listen_sa = (void *) sin; s->nautocmds = 0; s->autocmds = 0; #ifdef NO_SOCKADDR_LEN s->listen_sa_len = sizeof(*sin); #else sin->sin_len = sizeof(*sin); #endif add_socket(s); } else if ((l == 3) && !bcmp(cp,"cmd",3)) { if (! s) err("`cmd' line without a preceding `port' line"); while (*lp && Cisspace(*lp)) lp++; add_autocmd(s,lp); } else { err("unrecognized keyword `%.*s'",l,cp); } } fclose(f); } static void setup_sockets(void) { if (Cisdigit(*portstr)) { setup_sockets_compat(); } else { setup_sockets_config(); } } void shovechars(void) { fd_set input_set; fd_set output_set; long int now; struct timeval last_slice; struct timeval current_time; struct timeval next_slice; struct timeval timeout; struct timeval slice_timeout; struct descriptor_data *d; struct descriptor_data *dnext; int srv; int serr; int i; sockets = 0; nsockets = 0; setup_sockets(); createpidfile(); gettimeofday(&last_slice,0); maxdesc = getdtablesize() - 5; while (! shutdown_flag) { top:; gettimeofday(¤t_time,0); last_slice = update_quotas(last_slice,current_time); process_commands(); if (shutdown_flag) break; timeout.tv_sec = 1000; timeout.tv_usec = 0; next_slice = msec_add(last_slice,COMMAND_TIME_MSEC); slice_timeout = timeval_sub(next_slice,current_time); FD_ZERO(&input_set); FD_ZERO(&output_set); for (i=0;is,&input_set); #ifdef ASYNC_HOSTNAMES FD_SET(hostname_fd,&input_set); #endif #ifdef ASYNC_USERNAMES FD_SET(username_fd,&input_set); #endif for (d=descriptor_list;d;d=d->next) { if (d->booted) { if (d->descriptor >= 0) close(d->descriptor); freeqs(d); cfree(d->hostname); cfree(d->username); *d->prev = d->next; if (d->next) d->next->prev = d->prev; free(d); goto top; } if (d->waiting) continue; if (d->input.head) { timeout = slice_timeout; } else { FD_SET(d->descriptor,&input_set); } if (d->output.head) FD_SET(d->descriptor,&output_set); } unblock_alarm(); srv = select(FD_SETSIZE,&input_set,&output_set,0,&timeout); serr = errno; block_alarm(); if (srv < 0) { if (serr != EINTR) { log_status("select() error: %s",strerror(serr)); perror("select"); return; } } else { now = curtm(); accept_a_conn(&input_set); #ifdef ASYNC_HOSTNAMES if (FD_ISSET(hostname_fd,&input_set)) { process_hostname_output(); continue; /* because p_h_o() can boot a locked out descriptor */ } #endif #ifdef ASYNC_USERNAMES if (FD_ISSET(username_fd,&input_set)) { process_username_output(); continue; /* because p_u_o() can boot a locked out descriptor */ } #endif for (d=descriptor_list;d;d=dnext) { dnext = d->next; if (FD_ISSET(d->descriptor,&input_set)) { d->last_time = now; if (! process_input(d)) { shutdownsock(d); continue; } } if (FD_ISSET(d->descriptor,&output_set)) { if (! process_output(d)) shutdownsock(d); } } } } } struct descriptor_data *new_connection(int accsock) { int newsock; struct sockaddr_in remaddr; struct sockaddr_in lcladdr; socklen_t addr_len; struct descriptor_data *d; addr_len = sizeof(remaddr); newsock = accept(accsock,(struct sockaddr *)&remaddr,&addr_len); if (newsock < 0) { if (errno != EINTR) { fprintf(stderr,"%s: new connection accept: %s\n",argvec[0],strerror(errno)); } return(0); } if (addr_len != sizeof(remaddr)) { fprintf(stderr,"%s: peer address size wrong (%d, wanted %d)\n",argvec[0],addr_len,(int)sizeof(remaddr)); close(newsock); return(0); } if (remaddr.sin_family != AF_INET) { fprintf(stderr,"%s: local address isn't INET (af %d)\n",argvec[0],remaddr.sin_family); close(newsock); return(0); } addr_len = sizeof(lcladdr); if (getsockname(newsock,(struct sockaddr *)&lcladdr,&addr_len) < 0) { fprintf(stderr,"%s: new connection getsockname: %s\n",argvec[0],strerror(errno)); close(newsock); return(0); } if (addr_len != sizeof(lcladdr)) { fprintf(stderr,"%s: local address size wrong (%d, wanted %d)\n",argvec[0],addr_len,(int)sizeof(lcladdr)); close(newsock); return(0); } if (lcladdr.sin_family != AF_INET) { fprintf(stderr,"%s: local address isn't INET (af %d)\n",argvec[0],lcladdr.sin_family); close(newsock); return(0); } MALLOC(d,struct descriptor_data,1); d->descriptor = newsock; d->connected = 0; d->booted = 0; /* not set because no need: wordfold, waiting, hostnamewait, usernamewait */ d->telnetstate = TNS_DONT; d->windows_kludge = 0; d->player = NOTHING; make_nonblocking(newsock); d->output_prefix = 0; d->output_suffix = 0; d->output_fold = 0; d->output_col = 0; d->output_size = 0; d->output.head = 0; d->output.tail = &d->output.head; d->input.head = 0; d->input.tail = &d->input.head; d->raw_input = 0; d->raw_input_at = 0; d->quota = COMMAND_BURST_SIZE; d->last_time = 0; d->connected_at = curtm(); if (descriptor_list) descriptor_list->prev = &d->next; d->next = descriptor_list; d->prev = &descriptor_list; descriptor_list = d; d->hostname = 0; d->username = 0; if (newsock >= maxdesc) { freeqs(d); queue_string(d,NO_DESC_MESSAGE); process_output(d); shutdownsock(d); close(newsock); d->descriptor = -1; } log_status("ACCEPT: from %s(%d) on port %d, descriptor %d",inet_ntoa(remaddr.sin_addr),ntohs(remaddr.sin_port),ntohs(lcladdr.sin_port),newsock); d->hostnamewait = 0; d->usernamewait = 0; #if defined(SYNC_HOSTNAMES) { struct hostent *hp; hp = gethostbyaddr(&remaddr.sin_addr,sizeof(remaddr.sin_addr),AF_INET); if (hp) { d->hostname = dup_string(hp->h_name) log_status("HOSTNAME: %s -> %s for descriptor %d",inet_ntoa(remaddr.sin_addr),d->hostname,newsock); } else { d->hostname = dup_string(inet_ntoa(remaddr.sin_addr);) log_status("HOSTNAME: %s not found for descriptor %d",inet_ntoa(remaddr.sin_addr),newsock); } } #elif defined(ASYNC_HOSTNAMES) { struct in_addr *t; d->hostnamewait = 1; t = malloc(sizeof(remaddr.sin_addr)); *t = remaddr.sin_addr; d->hostname = (const void *) t; write(hostname_fd,t,sizeof(*t)); } #else d->hostname = dup_string(inet_ntoa(remaddr.sin_addr)); #endif #if defined(SYNC_USERNAMES) { d->username = dup_string(run_identd(&lcladdr,&addr)); log_status("USERNAME: %s for descriptor %d",d->username,newsock); } #elif defined(ASYNC_USERNAMES) { struct sockaddr_in *t; t = malloc(2*sizeof(struct sockaddr_in)); t[0] = lcladdr; t[1] = remaddr; d->usernamewait = 1; d->username = (const void *) t; write(username_fd,t,2*sizeof(struct sockaddr_in)); } #else d->username = dup_string("(no usernames)"); #endif check_waits(d); return(d); } static void clearstrings(struct descriptor_data *d) { if (d->output_prefix) { free(d->output_prefix); d->output_prefix = 0; } if (d->output_suffix) { free(d->output_suffix); d->output_suffix = 0; } } void shutdownsock(struct descriptor_data *d) { if (d->connected) { #ifdef MUFCONNECTS char dnbuf[64]; sprintf(&dnbuf[0],"%d",d->descriptor); run_global_hook(d->player,"do_disconnect",&dnbuf[0],makearray(1,makeinst(PROG_STRING,dup_string("disconnect")))); process_output(d); #endif log_status("DISCONNECT: descriptor %d player %s(%d)",d->descriptor,NAME(d->player),d->player); announce_disconnect(d->player); } else { log_status("DISCONNECT: descriptor %d never connected.",d->descriptor); } clearstrings(d); d->booted = 1; } static struct text_block *make_text_block(const char *s, int n, int esc) { struct text_block *p; MALLOC(p,struct text_block,1); if (esc) { const unsigned char *cp; unsigned char *tp; int realn; int i; realn = n; cp = (const unsigned char *) s; for (i=0;ibuf,char,realn); if (realn == n) { bcopy(s,p->buf,n); } else { tp = (unsigned char *) p->buf; cp = (const unsigned char *) s; for (i=0;inchars = realn; } else { MALLOC(p->buf,char,n); bcopy(s,p->buf,n); p->nchars = n; } p->start = p->buf; p->nxt = 0; return(p); } static void free_text_block(struct text_block *t) { free(t->buf); free(t); } static void add_to_queue(struct text_queue *q, const char *b, int n, int esc) { struct text_block *p; if (n == 0) return; p = make_text_block(b,n,esc); p->nxt = 0; *q->tail = p; q->tail = &p->nxt; } static int flush_queue(struct text_queue *q, int n) { struct text_block *p; int really_flushed; really_flushed = 0; n += strlen(FLUSHED_MESSAGE); while ((n > 0) && (p = q->head)) { n -= p->nchars; really_flushed += p->nchars; q->head = p->nxt; free_text_block(p); } p = make_text_block(FLUSHED_MESSAGE,strlen(FLUSHED_MESSAGE),0); p->nxt = q->head; q->head = p; if (! p->nxt) q->tail = &p->nxt; really_flushed -= p->nchars; return(really_flushed); } static void queue_write_(struct descriptor_data *d, const char *b, int n, int esc) { int space; space = MAX_OUTPUT - d->output_size - n; if (space < 0) { if (no_output_flushed) { while ( process_output(d) && d->output.head && (d->output_size+n >= MAX_OUTPUT) ) nap(1); } else { d->output_size -= flush_queue(&d->output,-space); } } if (d->output_fold) { const char *bp; const char *end; int c; c = d->output_col; end = b + n; bp = b; while (bp < end) { if ((*bp == '\r') || (*bp == '\n')) { c = 0; bp ++; } else { c ++; if (c > d->output_fold) { if (d->wordfold) { const char *t; for (t=bp;(t>b)&&!Cisspace(*t);t--) ; if (t == b) { if (bp > b) add_to_queue(&d->output,b,bp-b,esc); b = bp; } else { bp = t + 1; while ((t > b) && Cisspace(*t)) t --; if (! Cisspace(*t)) add_to_queue(&d->output,b,(t+1)-b,esc); for (b=bp;(b b) add_to_queue(&d->output,b,bp-b,esc); b = bp; } add_to_queue(&d->output,"\r\n",2,0); c = 0; } else { bp ++; } } } d->output_col = c; if (bp > b) add_to_queue(&d->output,b,bp-b,esc); } else { add_to_queue(&d->output,b,n,esc); d->output_size += n; } } void queue_write(struct descriptor_data *d, const void *b, int n) { queue_write_(d,b,n,d->telnetstate!=TNS_DONT); } static void queue_write_noesc(struct descriptor_data *d, const void *b, int n) { queue_write_(d,b,n,0); } void queue_string(struct descriptor_data *d, const char *s) { queue_write(d,s,strlen(s)); } int process_output(struct descriptor_data *d) { struct text_block *tbp; struct iovec iov[WRITEV_MAXCNT]; int niov; int did; int want; if (d->booted) return(0); tbp = d->output.head; if (! tbp) return(1); niov = 0; want = 0; while (1) { if ((niov >= WRITEV_MAXCNT) || ((niov > 0) && !tbp)) { did = writev(d->descriptor,&iov[0],niov); if (did < 0) { return(errno==EWOULDBLOCK); } d->output_size -= did; want -= did; while (did > 0) { if (did >= d->output.head->nchars) { struct text_block *tmp; tmp = d->output.head; did -= tmp->nchars; d->output.head = tmp->nxt; free_text_block(tmp); if (d->output.head == 0) d->output.tail = &d->output.head; } else { d->output.head->start += did; d->output.head->nchars -= did; break; } } if (want > 0) return(1); niov = 0; } if (! tbp) break; iov[niov].iov_base = tbp->start; iov[niov].iov_len = tbp->nchars; want += tbp->nchars; niov ++; tbp = tbp->nxt; } return(1); } void make_nonblocking(int s) { if (fcntl (s, F_SETFL, FNDELAY) == -1) { perror ("make_nonblocking: fcntl"); panic ("FNDELAY fcntl failed"); } } void freeqs(struct descriptor_data *d) { struct text_block *cur; struct text_block *next; cur = d->output.head; while (cur) { next = cur->nxt; free_text_block(cur); cur = next; } d->output.head = 0; d->output.tail = &d->output.head; cur = d->input.head; while (cur) { next = cur->nxt; free_text_block(cur); cur = next; } d->input.head = 0; d->input.tail = &d->input.head; if (d->raw_input) free(d->raw_input); d->raw_input = 0; d->raw_input_at = 0; } static void goodbye_user(struct descriptor_data *d) { write (d->descriptor, LEAVE_MESSAGE, strlen (LEAVE_MESSAGE)); } char *strsave (const char *s) { char *p; MALLOC (p, char, strlen(s) + 1); if (p) strcpy (p, s); return p; } void save_command(struct descriptor_data *d, const void *command) { add_to_queue(&d->input,command,strlen(command)+1,0); } inline static int stripped_char(unsigned char c) { return( ((c < 32) && (c != '\t')) || ((c >= 127) && (c < 160)) ); } inline static int delete_char(unsigned char c) { return( (c == 8) || (c == 127) ); } int process_input(struct descriptor_data *d) { unsigned char buf[1024]; int got; unsigned char *p; unsigned char *pend; unsigned char *q; unsigned char *qend; unsigned char reply[3]; got = read(d->descriptor,&buf[0],sizeof(buf)); if (d->booted || (got <= 0)) return(0); seed_random(&buf[0],got); if (! d->raw_input) { MALLOC(d->raw_input,unsigned char,MAX_COMMAND_LEN); d->raw_input_at = d->raw_input; } p = d->raw_input_at; pend = d->raw_input + MAX_COMMAND_LEN - 1; for (q=buf,qend=buf+got;qtelnetstate) { case TNS_DATA: if (*q == TN_IAC) { d->telnetstate = TNS_IAC; break; } /* fall through */ case TNS_DONT: if (*q == '\n') { *p = '\0'; #ifdef DROP_EMPTY_LINES if (p > d->raw_input) #endif save_command(d,d->raw_input); p = d->raw_input; } else if (d->windows_kludge && delete_char(*q)) { if (p > d->raw_input) p --; } else if ((p < pend) && !stripped_char(*q)) { *p++ = *q; } break; case TNS_IAC: switch (*q) { case TN_IAC: *p++ = TN_IAC; d->telnetstate = TNS_DATA; break; case TN_DO: d->telnetstate = TNS_DO; break; case TN_WILL: d->telnetstate = TNS_WILL; break; case TN_DONT: case TN_WONT: d->telnetstate = TNS_SKIP; break; case TN_SB: d->telnetstate = TNS_SB; break; default: d->telnetstate = TNS_DATA; break; } break; case TNS_DO: reply[0] = TN_IAC; reply[1] = TN_WONT; reply[2] = *q; queue_write_noesc(d,&reply[0],3); d->telnetstate = TNS_DATA; break; case TNS_WILL: reply[0] = TN_IAC; reply[1] = TN_DONT; reply[2] = *q; queue_write_noesc(d,&reply[0],3); d->telnetstate = TNS_DATA; break; case TNS_SKIP: d->telnetstate = TNS_DATA; break; case TNS_SB: if (*q == TN_IAC) d->telnetstate = TNS_SBI; break; case TNS_SBI: d->telnetstate = (*q == TN_SE) ? TNS_DATA : TNS_SB; break; default: fprintf(stderr,"descriptor %d telnetstate %d\n",d->descriptor,d->telnetstate); d->telnetstate = TNS_DONT; break; } } if (p > d->raw_input) { d->raw_input_at = p; } else { free(d->raw_input); d->raw_input = 0; d->raw_input_at = 0; } return(1); } void set_userstring(char **userstring, const char *command) { if (*userstring) { free(*userstring); *userstring = 0; } while (*command && isascii(*command) && Cisspace(*command)) command++; if (*command) *userstring = strsave(command); } void process_commands() { int nprocessed; struct descriptor_data *d; struct descriptor_data *dnext; struct text_block *t; alarm_block = 1; do { nprocessed = 0; for (d=descriptor_list;d;d=dnext) { dnext = d->next; if ( d->connected && DBFETCH(d->player)->sp.player.run && !DBFETCH(d->player)->sp.player.run->reading ) { interp_resume(d->player); } else { t = d->input.head; if ((d->quota > 0) && t) { d->quota --; nprocessed ++; if (!d->booted && !do_command(d,t->start)) { shutdownsock(d); } else { d->input.head = t->nxt; if (!d->input.head) d->input.tail = &d->input.head; free_text_block(t); } } } } } while (nprocessed > 0); big_fat_descripto_lock = 0; alarm_block = 0; if (alarm_triggered) time_keeper(); } static void boolean_conncmd(struct descriptor_data *d, const char *cmdrest, const char *cmd, void (*ifno)(struct descriptor_data *), void (*ifyes)(struct descriptor_data *), int isyes) { const char *cp; cp = cmdrest; while (*cp && isascii(*cp) && Cisspace(*cp)) cp ++; switch (*cp) { case 'y': case 'Y': case '1': case 't': case 'T': (*ifyes)(d); break; case 'n': case 'N': case '0': case 'f': case 'F': (*ifno)(d); break; case '?': queue_string(d,isyes?"Y\r\n":"N\r\n"); break; case '\0': queue_string(d,cmd); queue_string(d," [yY1tTnN0fF?]\r\n"); break; } } int do_command(struct descriptor_data *d, char *command) { dbref i; dbref player; SERIAL playser; int count; struct descriptor_data *d2; clearforce(); player = d->player; if (d->connected) { struct propref *pr; pr = lookup_property(player,".last",LP_CREATE,PPERM_COMPAT_PRIVATE); propref_set_type_and_value(pr,PTYPE_STRING,command); propref_done(pr); } #ifdef CMDRING_SIZE cmdring_stash(d,command); #endif if (!strcmp(command,"QUIT")) { goodbye_user(d); process_output(d); return(0); } else if (!strncmp(command,"OUTPUTPREFIX",12)) { set_userstring(&d->output_prefix,command+12); } else if (!strncmp(command,"OUTPUTSUFFIX",12)) { set_userstring(&d->output_suffix,command+12); } else if (!strncmp(command,"OUTPUTFOLD",10)) { d->output_fold = atoi(command+10); if (d->output_fold < 0) d->output_fold = 0; d->wordfold = 0; } else if (!strncmp(command,"OUTPUTWRAP",10)) { d->output_fold = atoi(command+10); if (d->output_fold < 0) d->output_fold = 0; d->wordfold = 1; } else if (!strncmp(command,"TELNET",6)) { boolean_conncmd(d,command+6,"TELNET", ({ void foo(struct descriptor_data *d) { d->telnetstate = TNS_DONT; } &foo; }), ({ void foo(struct descriptor_data *d) { if (d->telnetstate == TNS_DONT) { d->telnetstate = TNS_DATA; } } &foo; }), d->telnetstate!=TNS_DONT ); } else if ( !strncmp(command,"STUPID-WIN",10) || !strncmp(command,"STUPID_WIN",10) || !strncmp(command,"STUPIDWIN",9) ) { boolean_conncmd(d,command+((command[7]=='W')?10:9),"STUPID-WIN", ({ void foo(struct descriptor_data *d) { d->windows_kludge = 0; } &foo; }), ({ void foo(struct descriptor_data *d) { d->windows_kludge = 1; } &foo; }), d->windows_kludge ); } else if (!strncmp(command,"HOLDSHUT",8)) { boolean_conncmd(d,command+8,"HOLDSHUT", ({ void foo(struct descriptor_data *d) { d->holdshutdown = 0; } &foo; }), ({ void foo(struct descriptor_data *d) { d->holdshutdown = 1; } &foo; }), d->holdshutdown ); } else if (d->connected) { if (d->output_prefix) { queue_string(d,d->output_prefix); queue_write(d,"\r\n",2); } playser = DBFETCH(player)->serial; process_command(player,command); if (DBFETCH(player)->serial != playser) return(0); /* make sure we haven't been booted off... */ for (d2=descriptor_list;d2;d2=d2->next) if (d2 == d) break; if (d2 && d->output_suffix) { queue_string(d,d->output_suffix); queue_write(d,"\r\n",2); } } else { char cmd[MAX_COMMAND_LEN]; char user[MAX_COMMAND_LEN]; char password[MAX_COMMAND_LEN]; int runcount; parse_connect(command,&cmd[0],&user[0],&password[0]); if (!cmd[0]) { } else if (!string_compare(&cmd[0],"connect")) { con_connect(d,&user[0],&password[0]); } else if (!string_compare(&cmd[0],"create")) { con_create(d,&user[0],&password[0]); } else { runcount = 0; DOLIST(i,DBFETCH(GLOBAL_ENVIRONMENT)->sp.room.exits) { if (FLAGS(i) & UNCONNECTED) { char *xn; int lxn; SERIAL iser; iser = DBFETCH(i)->serial; xn = DBFETCH(i)->name; lxn = strlen(xn); if ( string_prefix(command,xn) && (!command[lxn] || Cisspace(command[lxn])) ) { for (count=0;countsp.exit.ndest;count++) { if (Typeof(DBFETCH(i)->sp.exit.dest[count]) == TYPE_PROGRAM) { if (runcount == 0) { if (d->output_prefix) { queue_string(d,d->output_prefix); queue_write(d,"\r\n",2); } } unconnected_interp(d->descriptor,DBFETCH(i)->sp.exit.dest[count],i,command+lxn,xn); if (DBFETCH(i)->serial != iser) goto out; /* break 2 */ runcount ++; } } } } } out:; if (runcount == 0) { #ifdef BUILTIN_WHO if (!strcmp(&cmd[0],"WHO")) { if (d->output_prefix) { queue_string(d,d->output_prefix); queue_write(d,"\r\n",2); } dump_users(NOTHING,command+3,dump_to_desc,d); runcount = 1; } else #endif if (string_prefix(&cmd[0],"co")) { con_connect(d,&user[0],&password[0]); runcount = -1; } else if (string_prefix(&cmd[0],"cr")) { con_create(d,&user[0],&password[0]); runcount = -1; } } if (runcount > 0) { if (d->output_suffix) { queue_string(d,d->output_suffix); queue_write(d,"\r\n",2); } } else if (runcount == 0) { welcome_user(d); } } } return(1); } void con_connect(struct descriptor_data *d, const char *user, const char *pw) { dbref player; SERIAL playser; player = connect_player(user,pw); if (player == NOTHING) { queue_string(d,CONNECT_FAIL); log_status("FAILED CONNECT %s on descriptor %d",user,d->descriptor); } #ifdef NOLOGINS else if (no_login(d,player)) { log_status("FAILED CONNECT-NOLOGIN %s on descriptor %d",user,d->descriptor); } #endif /*NOLOGINS*/ else { int runprog; int mufedit; int mufinsert; char buf[512]; runprog = 0; mufedit = 0; mufinsert = 0; if (FLAGS(player) & INTERACTIVE) { if (DBFETCH(player)->sp.player.run) runprog = 1; else if (DBFETCH(player)->sp.player.insert_mode) mufinsert = 1; else mufedit = 1; } log_status("CONNECTED: %s(%d) on descriptor %d",NAME(player),player,d->descriptor); d->connected = 1; d->connected_at = curtm(); d->player = player; announce_connect(player); #ifdef MUFCONNECTS { char dnbuf[64]; sprintf(&dnbuf[0],"%d",d->descriptor); run_global_hook(player,"do_connect",&dnbuf[0],makearray(1,makeinst(PROG_STRING,dup_string("connect")))); } #endif #ifdef TIMESTAMPS DBFETCH(player)->time_used = d->connected_at; #endif playser = DBFETCH(player)->serial; do_look_around(player); if (DBFETCH(player)->serial != playser) { process_output(d); shutdownsock(d); return; } buf[0] = '\0'; if (runprog) { sprintf(&buf[0],"*** You are currently using a program. Use \"%s\" to return to a more reasonable state of control. ***",BREAK_COMMAND); } else if (mufinsert) { sprintf(&buf[0],"*** You are currently inserting program text. Use \".\" to return to the editor, then \"%c\" if you wish to return to your regularly scheduled Muck universe. ***",QUIT_EDIT_COMMAND); } else if (mufedit) { sprintf(&buf[0],"*** You are currently using the MUF program editor. ***"); } if (buf[0]) notify(player,buf); } } #ifndef REGISTRATION void con_create(struct descriptor_data *d, const char *user, const char *pw) { dbref player; SERIAL playser; player = create_player(user,pw); if (player == NOTHING) { queue_string(d,CREATE_FAIL); log_status("FAILED CREATE %s on descriptor %d",user,d->descriptor); } else { log_status("CREATED %s(%d) on descriptor %d",NAME(player),player,d->descriptor); d->connected = 1; d->connected_at = curtm(); d->player = player; announce_connect(player); playser = DBFETCH(player)->serial; do_look_around(player); if (DBFETCH(player)->serial != playser) { process_output(d); shutdownsock(d); } } } #else /* REGISTRATION */ void con_create(struct descriptor_data *d, const char *user, UNUSED_ARG(const char *pw)) { queue_string(d,REG_MSG); log_status("FAILED CREATE %s on descriptor %d",user,d->descriptor); } #endif /* REGISTRATION */ void parse_connect (const char *msg, char *command, char *user, char *pass) { char *p; while (*msg && isascii(*msg) && Cisspace (*msg)) msg++; p = command; while (*msg && isascii(*msg) && !Cisspace (*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii(*msg) && Cisspace (*msg)) msg++; p = user; while (*msg && isascii(*msg) && !Cisspace (*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii(*msg) && Cisspace (*msg)) msg++; p = pass; while (*msg && isascii(*msg) && !Cisspace (*msg)) *p++ = *msg++; *p = '\0'; } int boot_off(dbref player) { struct descriptor_data *d; struct descriptor_data *next; for (d = descriptor_list; d; d = next) { next = d->next; if (d->connected && !d->booted && (d->player == player)) { process_output (d); shutdownsock(d); return 1; } } return 0; } int dboot_off(int desc) { struct descriptor_data *d; struct descriptor_data *next; for (d = descriptor_list; d; d = next) { next = d->next; if ( d->descriptor == desc) { process_output (d); shutdownsock(d); return 1; } } return 0; } void close_sockets() { struct descriptor_data *d; int i; while (descriptor_list) { d = descriptor_list; descriptor_list = d->next; queue_string(d,shutdown_message); process_output(d); clearstrings(d); freeqs(d); cfree(d->hostname); cfree(d->username); if ( (d->descriptor >= 0) && (!shutdown_flag || !d->holdshutdown) ) close(d->descriptor); free(d); } for (i=0;is); free(sockets[i]); } free(sockets); nsockets = 0; } void emergency_shutdown() { close_sockets(); } void bailout(int sig) { char message[1024]; shutdown_message = CRASHING_MESSAGE; setpanicmalloc(); sprintf(message,"BAILOUT: caught signal %d",sig); panic(message); _exit(7); } void dump_descriptors(dbref player, const char *user) { struct descriptor_data *d; int wizard; int players; long int now; char buf[2048]; char pbuf[64]; char *bp; int doit; int loc; int n; int l; wizard = Wizard(player); if (! wizard) { notify(player,"Sorry."); return; } while (*user && Cisspace(*user)) user++; if (!*user) user = 0; now = curtm(); notify(player,"Player Name Desc. Loc. On For Idle From"); players = 0; for (d=descriptor_list;d;d=d->next) { doit = 0; if (d->connected) { players ++; if (!user || string_prefix(NAME(d->player),user)) { sprintf(&pbuf[0],"%.*s(#%d)",PLAYER_NAME_LIMIT,NAME(d->player),(int)d->player); loc = DBFETCH(d->player)->location; doit = 1; } } else { pbuf[0] = '\0'; loc = -1; doit = 1; } if (doit) { bp = &buf[0]; sprintf(bp,"%-*s [%4d] [%6d] %5s %4s ", PLAYER_NAME_LIMIT+9, &pbuf[0], d->descriptor, loc, time_format_1(now-d->connected_at), time_format_2(now-d->last_time) ); bp += strlen(bp); l = 20; #ifdef USERNAMES if (d->usernamewait) { bcopy("???",bp,3); bp += 3; l -= 3; } else { int m; n = strlen(d->username); m = d->hostnamewait ? 3 : strlen(d->hostname); if ((n+1+m > l) && (n > 10)) n = 10; bcopy(d->username,bp,n); bp += n; l -= n; } *bp++ = '@'; l --; #endif if (d->hostnamewait) { bcopy("???",bp,3); bp += 3; } else { n = strlen(d->hostname); if (n > l) n = l; bcopy(d->hostname,bp,n); bp += n; } *bp = '\0'; notify(player,&buf[0]); } } sprintf(&buf[0],"%d %s connected.", players, (players==1)?"player is":"players are" ); notify(player,&buf[0]); } void dump_to_desc(void *arg, const char *str) { queue_string((struct descriptor_data *)arg,str); queue_write((struct descriptor_data *)arg,"\r\n",2); } void dump_to_player(void *arg, const char *str) { notify_listener(*(dbref *)arg,*(dbref *)arg,str); } #ifdef BUILTIN_WHO #define LW 78 /* line width */ void dump_users( dbref dumper, const char *user, void (*eachline)(void *, const char *), void *arg) { struct descriptor_data *d; int wizard; int pcount; long now; char buf[2048]; char *bp; int l; int m; wizard = PERMIT_EXTENDEDWHO(dumper); while (*user && isspace(*user)) user++; if (!*user) user = NULL; now = curtm(); if (wizard) { sprintf(&buf[0],"%-*sWhere &d On For Idle From",2+LW-2-5-4-9-5-1-24,"Player"); (*eachline)(arg,&buf[0]); } else { (*eachline)(arg,"Player Name On For Idle Doing"); } pcount = 0; for (d=descriptor_list;d;d=d->next) { if ( d->connected && PERMIT_SEEWHO(dumper,d->player) && (pcount++,1) && (!user || string_prefix(NAME(d->player),user)) ) { bp = &buf[0]; if (wizard) { sprintf(bp,"%s%s%-*s%5d%4d%9s%5s ", (FLAGS(d->player)&UNSEEN)?"*":" ", (FLAGS(d->player)&INTERACTIVE)?"+":" ", LW-2-5-4-9-5-1-24, unparse_object(dumper,d->player), (int)DBFETCH(d->player)->location, d->descriptor, time_format_1(now-d->connected_at), time_format_2(now-d->last_time) ); bp += strlen(bp); l = 24; m = strlen(d->hostname); #ifdef USERNAMES { int n; n = strlen(d->username); if ((n+1+m > l) && (n > 12)) n = 12; bcopy(d->username,bp,n); bp += n; *bp++ = '@'; l -= n + 1; } #endif if (m > l) m = l; bcopy(d->hostname,bp,m); bp += m; *bp = '\0'; } else { char *doing; doing = get_property_value(d->player,PROP_RDONLY,"doing"); sprintf(buf,"%s%s%-19s%9s%5s %-.41s", (FLAGS(d->player)&UNSEEN)?"*":" ", (FLAGS(d->player)&INTERACTIVE)?"+":" ", NAME(d->player), time_format_1(now-d->connected_at), time_format_2(now-d->last_time), DoNullInd(doing) ); free(doing); } (*eachline)(arg,&buf[0]); } } sprintf(buf,"%d player%s connected.",pcount,(pcount==1)?" is":"s are"); (*eachline)(arg,&buf[0]); } #undef LW #endif #ifdef BUILTIN_WHO void do_who(dbref player, char *arg1, char *arg2) { dump_users(player,arg1,dump_to_player,&player); } #endif char *time_format_1(long dt) { struct tm *delta; static char buf[64]; delta = gmtime(&dt); if (delta->tm_yday > 0) { sprintf(&buf[0],"%dd %02d:%02d",delta->tm_yday,delta->tm_hour,delta->tm_min); } else { sprintf(&buf[0],"%02d:%02d",delta->tm_hour,delta->tm_min); } return(&buf[0]); } char *time_format_2(long dt) { static char buf[64]; if (dt >= 60*60*24) { sprintf(&buf[0],"%ldd",dt/(60*60*24)); } else if (dt >= 60*60) { sprintf(&buf[0],"%dh",(int)(dt/(60*60))); } else if (dt >= 60) { sprintf(&buf[0],"%dm",(int)(dt/60)); } else { sprintf(&buf[0],"%ds",(int)dt); } return(&buf[0]); } char *time_format_3(long dt) { static char buf[64]; char *bp; bp = &buf[0]; #define FOO(factor,ch) \ if (dt >= (factor)) { sprintf(bp,"%ld" #ch,dt/(factor)); bp += strlen(bp); dt %= factor; } FOO(60*60*24,d) FOO(60*60,h); FOO(60,m); if (dt || (bp == &buf[0])) sprintf(bp,"%lds",dt); return(&buf[0]); } static int set_private(dbref d) { struct propref *pr; if ((Typeof(d) == TYPE_PLAYER) && (FLAGS(d) & DARK)) return(1); pr = lookup_property(d,"_private",0); if (! pr) pr = lookup_property(d,"private",0); if (! pr) pr = lookup_property(d,"_dark",0); if (! pr) pr = lookup_property(d,"dark",0); if (pr) { propref_done(pr); return(1); } return(0); } void announce_connect(dbref player) { dbref loc; dbref i; const char *locstr; char buf[BUFFER_LEN]; loc = getloc(player); if (loc == NOTHING) return; locstr = (set_private(player) || set_private(loc)) ? "a private location" : NAME(loc); for (i=0;ipw_uid) < 0) { int e; e = errno; log_status("can't setuid(%s->%d): %m",MUD_ID,pw->pw_uid,e); errno = e; perror("setuid"); exit(1); } } #endif void welcome_user(struct descriptor_data *d) { if (spit_file_d(WELC_FILE,d)) { queue_string(d,DEFAULT_WELCOME_MESSAGE); fprintf(stderr,"welcome_user: %s: %s\n",WELC_FILE,strerror(errno)); } }