/* bsd.c */ #include "copyright.h" #include <stdio.h> #ifdef WANT_ANSI #ifdef __STDC__ #include <stdlib.h> #endif /* __STDC__ */ #endif /* WANT_ANSI */ #include <string.h> #ifdef VMS #include "multinet_root:[multinet.include.sys]file.h" #include "multinet_root:[multinet.include.sys]ioctl.h" #include "multinet_root:[multinet.include]errno.h" #else #include <sys/file.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <fcntl.h> #include <sys/errno.h> #endif #include <signal.h> #include <ctype.h> #include "mudconf.h" #include "config.h" #include "db.h" #include "externs.h" #include "interface.h" #include "flags.h" extern int errno; extern void dispatch(); static int sock; int ndescriptors = 0; DESC *descriptor_list = NULL; DESC *initializesock(); DESC *new_connection(); int process_output(); int process_input(); int make_socket(int port) { int s; struct sockaddr_in server; int opt; s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("creating stream socket"); exit(3); } opt = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) { perror("setsockopt"); } server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(port); if (bind(s, (struct sockaddr *) & server, sizeof(server))) { perror("binding stream socket"); close(s); exit(4); } listen(s, 5); return s; } void shovechars(int port) { fd_set input_set, output_set; time_t now; struct timeval last_slice, current_time; struct timeval next_slice; struct timeval timeout, slice_timeout; int maxd, found; DESC *d, *dnext, *newd; int avail_descriptors; mudstate.debug_cmd = (char *)"< shovechars >"; nhashinit(&mudstate.desc_htab, 37); sock = make_socket(port); maxd = sock + 1; gettimeofday(&last_slice, (struct timezone *) 0); avail_descriptors = getdtablesize() - 4; while (mudstate.shutdown_flag == 0) { gettimeofday(¤t_time, (struct timezone *) 0); last_slice = update_quotas(last_slice, current_time); process_commands(); if (mudstate.shutdown_flag) break; /* test for events */ dispatch(); /* any queued robot commands waiting? */ timeout.tv_sec = test_top() ? 0 : 1000; timeout.tv_usec = 0; next_slice = msec_add(last_slice, mudconf.timeslice); slice_timeout = timeval_sub(next_slice, current_time); FD_ZERO(&input_set); FD_ZERO(&output_set); if (ndescriptors < avail_descriptors) FD_SET(sock, &input_set); DESC_ITER_ALL(d) { if (!d->input_head) FD_SET(d->descriptor, &input_set); if (d->output_head) FD_SET(d->descriptor, &output_set); } if ((found = select(maxd, &input_set, &output_set, (fd_set *)NULL, &timeout)) < 0) { if (errno != EINTR) { perror("select"); } } else { /* if !found then time for robot commands */ if (!found) { do_top(mudconf.queue_chunk); continue; } else { do_top(mudconf.active_q_chunk); } (void) time(&now); if (FD_ISSET(sock, &input_set)) { if (!(newd = new_connection(sock))) { if (errno && errno != EINTR && errno != EMFILE && errno != ENFILE) { perror("new_connection"); } } else { if (newd->descriptor >= maxd) maxd = newd->descriptor + 1; } } for (d = descriptor_list; d; d = dnext) { dnext = d->next; if (FD_ISSET(d->descriptor, &input_set)) { d->last_time = now; if (d->flags & DS_AUTODARK) { d->flags &= ~DS_AUTODARK; s_Flags(d->player, Flags(d->player) & ~DARK); } if (!process_input(d)) { shutdownsock(d, R_SOCKDIED); continue; } } if (FD_ISSET(d->descriptor, &output_set)) { if (!process_output(d)) { shutdownsock(d, R_SOCKDIED); } } } } } } const char *addrout(struct in_addr a) { extern char *inet_ntoa(); if(mudconf.use_hostname) { struct hostent *he; he = gethostbyaddr(&a.s_addr, sizeof(a.s_addr), AF_INET); if (he) return he->h_name; else return (inet_ntoa(a)); } else return (inet_ntoa(a)); } DESC *new_connection(int sock) { int newsock; char *buff, *buff1, *cmdsave; struct sockaddr_in addr; int addr_len; DESC *d; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *)"< new_connection >"; addr_len = sizeof(addr); newsock = accept(sock, (struct sockaddr *) & addr, &addr_len); if (newsock < 0) { return 0; } else if (site_check(addr.sin_addr, mudstate.access_list) == H_FORBIDDEN) { STARTLOG(LOG_NET|LOG_SECURITY,"NET","SITE") buff=alloc_mbuf("new_connection.LOG.badsite"); sprintf(buff, "[%d/%s] Connection refused. (Remote port %d)", newsock, addrout(addr.sin_addr), ntohs(addr.sin_port)); log_text(buff); free_mbuf(buff); ENDLOG dump_text(newsock, mudstate.site_fcache); shutdown(newsock, 2); close(newsock); errno = 0; d = NULL; } else { buff = alloc_mbuf("new_connection.sitename"); strcpy(buff, addrout(addr.sin_addr)); STARTLOG(LOG_NET,"NET","CONN") buff1=alloc_mbuf("new_connection.LOG.open"); sprintf(buff1, "[%d/%s] Connection opened (remote port %d)", newsock, buff, ntohs(addr.sin_port)); log_text(buff1); free_mbuf(buff1); ENDLOG d = initializesock(newsock, &addr, buff); free_mbuf(buff); mudstate.debug_cmd = cmdsave; } mudstate.debug_cmd = cmdsave; return(d); } /* Disconnect reasons that get written to the logfile */ static const char *disc_reasons[] = { "Unspecified", "Quit", "Inactivity Timeout", "Booted", "Remote Close or Net Failure", "Game Shutdown", "Login Retry Limit", "Logins Disabled", "Logout (Connection Not Dropped)", "Too Many Connected Players" }; /* Disconnect reasons that get fed to A_ADISCONNECT via announce_disconnect */ static const char *disc_messages[] = { "unknown", "quit", "timeout", "boot", "netdeath", "shutdown", "badlogin", "nologins", "logout" }; void shutdownsock(DESC *d, int reason) { char *buff, *buff2; time_t now; if ((reason == R_LOGOUT) && (site_check((d->address).sin_addr, mudstate.access_list) == H_FORBIDDEN)) reason = R_QUIT; if (d->flags & DS_CONNECTED) { /* Do the disconnect stuff if we aren't doing a LOGOUT * (which keeps the connection open so the player can connect * to a different character). */ if (reason != R_LOGOUT) { fcache_dump(d, mudstate.quit_fcache); STARTLOG(LOG_NET|LOG_LOGIN,"NET","DISC") buff=alloc_mbuf("shutdownsock.LOG.disconn"); sprintf(buff,"[%d/%s] Logout by ", d->descriptor, d->addr); log_text(buff); log_name(d->player); sprintf(buff," <Reason: %s>", disc_reasons[reason]); log_text(buff); free_mbuf(buff); ENDLOG } else { STARTLOG(LOG_NET|LOG_LOGIN,"NET","LOGO") buff=alloc_mbuf("shutdownsock.LOG.logout"); sprintf(buff,"[%d/%s] Logout by ", d->descriptor, d->addr); log_text(buff); log_name(d->player); sprintf(buff," <Reason: %s>", disc_reasons[reason]); log_text(buff); free_mbuf(buff); ENDLOG } /* If requested, write an accounting record of the form: * Plyr# Flags Cmds ConnTime Loc Money [Site] <DiscRsn> Name */ STARTLOG(LOG_ACCOUNTING,"DIS","ACCT") time(&now); now -= d->connected_at; buff=alloc_lbuf("shutdownsock.LOG.accnt"); buff2 = decode_flags(GOD, Flags(d->player), TYPE_PLAYER); sprintf(buff, "%d %s %d %d %d %d [%s] <%s> %s", d->player, buff2, d->command_count, now, Location(d->player), Pennies(d->player), d->addr, disc_reasons[reason], Name(d->player)); log_text(buff); free_lbuf(buff); free_sbuf(buff2); ENDLOG announce_disconnect(d->player, d, disc_messages[reason]); } else { if (reason == R_LOGOUT) reason = R_QUIT; STARTLOG(LOG_SECURITY|LOG_NET,"NET","DISC") buff=alloc_mbuf("shutdownsock.LOG.neverconn"); sprintf(buff, "[%d/%s] Connection closed, never connected. <Reason: %s>", d->descriptor, d->addr, disc_reasons[reason]); log_text(buff); free_mbuf(buff); ENDLOG } process_output(d); clearstrings(d); if (reason == R_LOGOUT) { d->flags &= ~DS_CONNECTED; d->connected_at = time(0); d->retries_left = mudconf.retry_limit; d->command_count = 0; d->timeout = mudconf.idle_timeout; d->player = 0; d->doing[0] = '\0'; d->quota = mudconf.cmd_quota_max; d->last_time = 0; d->host_info = site_check((d->address).sin_addr, mudstate.access_list) | site_check((d->address).sin_addr, mudstate.suspect_list); d->input_tot = d->input_size; d->output_tot = 0; welcome_user(d); } else { shutdown(d->descriptor, 2); close(d->descriptor); freeqs(d); *d->prev = d->next; if (d->next) d->next->prev = d->prev; free_desc(d); ndescriptors--; } } void make_nonblocking(int s) { struct linger ling; if (fcntl(s, F_SETFL, FNDELAY) == -1) { perror("make_nonblocking: fcntl"); panic("FNDELAY fcntl failed", 0); } ling.l_onoff = 0; ling.l_linger = 0; if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling)) < 0) { perror("setsockopt"); } } DESC *initializesock(int s, struct sockaddr_in *a, char *addr) { DESC *d; ndescriptors++; d = alloc_desc("init_sock"); d->descriptor = s; d->flags = 0; d->connected_at = time(0); d->retries_left = mudconf.retry_limit; d->command_count = 0; d->timeout = mudconf.idle_timeout; d->host_info = site_check((*a).sin_addr, mudstate.access_list) | site_check((*a).sin_addr, mudstate.suspect_list); d->player = 0; /* be sure #0 isn't wizard. Shouldn't be. */ d->addr[0] = '\0'; d->doing[0] = '\0'; make_nonblocking(s); d->output_prefix = NULL; d->output_suffix = NULL; d->output_size = 0; d->output_tot = 0; d->output_lost = 0; d->output_head = NULL; d->output_tail = NULL; d->input_head = NULL; d->input_tail = NULL; d->input_size = 0; d->input_tot = 0; d->input_lost = 0; d->raw_input = NULL; d->raw_input_at = NULL; d->quota = mudconf.cmd_quota_max; d->last_time = 0; strncpy(d->addr, addr, 50); d->address = *a; /* added 5/3/90 SCG */ if (descriptor_list) descriptor_list->prev = &d->next; d->hashnext = NULL; d->next = descriptor_list; d->prev = &descriptor_list; descriptor_list = d; welcome_user(d); return d; } void dump_text (int descriptor, FBLOCK *fb) { int cnt, remaining; char *start; while (fb != NULL) { start = fb->data; remaining = fb->hdr.nchars; while (remaining > 0) { #ifndef VMS cnt = write(descriptor, start, remaining); #else cnt = socket_write(descriptor, start, remaining); #endif if (cnt < 0) return; remaining -= cnt; start += cnt; } fb = fb->hdr.nxt; } return; } int process_output (DESC *d) { TBLOCK *tb, *save; int cnt; char *cmdsave; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *)"< process_output >"; tb = d->output_head; while (tb != NULL) { while (tb->hdr.nchars > 0) { #ifndef VMS cnt = write(d->descriptor, tb->hdr.start, tb->hdr.nchars); #else cnt = socket_write(d->descriptor, tb->hdr.start, tb->hdr.nchars); #endif if (cnt < 0) { mudstate.debug_cmd = cmdsave; if (errno == EWOULDBLOCK) return 1; return 0; } d->output_size -= cnt; tb->hdr.nchars -= cnt; tb->hdr.start += cnt; } save = tb; tb = tb->hdr.nxt; free_lbuf(save); d->output_head = tb; if (tb == NULL) d->output_tail = NULL; } mudstate.debug_cmd = cmdsave; return 1; } int process_input (DESC *d) { static char buf[LBUF_SIZE]; int got, in, lost; char *p, *pend, *q, *qend; char *cmdsave; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *)"< process_input >"; #ifndef VMS got = in = read(d->descriptor, buf, sizeof buf); #else got = in = socket_read(d->descriptor, buf, sizeof buf); #endif if (got <= 0) { mudstate.debug_cmd = cmdsave; return 0; } if (!d->raw_input) { d->raw_input = alloc_lbuf("process_input.raw"); d->raw_input_at = d->raw_input; } p = d->raw_input_at; pend = d->raw_input + LBUF_SIZE - sizeof(CBLKHDR) - 1; lost = 0; for (q=buf, qend=buf+got; q<qend; q++) { if (*q == '\n') { *p = '\0'; if (p > d->raw_input) { save_command(d, d->raw_input); } else { in -= 1; /* for newline */ } p = d->raw_input; } else if ((*q == '\b') || (*q == 8) || (*q == 127)) { if (*q == 127) queue_string(d, "\b \b"); else queue_string(d, " \b"); in -= 2; if (d->raw_input_at > d->raw_input) (d->raw_input_at)--; if (p > d->raw_input_at) p--; } else if (p < pend && isascii(*q) && isprint(*q)) { *p++ = *q; } else { in--; if (p >= pend) lost++; } } if (p > d->raw_input) { d->raw_input_at = p; } else { free_lbuf(d->raw_input); d->raw_input = NULL; d->raw_input_at = NULL; } d->input_tot += got; d->input_size += in; d->input_lost += lost; mudstate.debug_cmd = cmdsave; return 1; } void close_sockets(int emergency, char *message) { DESC *d, *dnext; do_rwho(NOTHING, NOTHING, RWHO_STOP); DESC_SAFEITER_ALL(d,dnext) { if (emergency) { #ifndef VMS write(d->descriptor, message, strlen(message)); #else socket_write(d->descriptor, message, strlen(message)); #endif if (shutdown(d->descriptor, 2) < 0) perror("shutdown"); close(d->descriptor); } else { queue_string(d, message); queue_write(d, "\r\n", 2); shutdownsock(d, R_GOING_DOWN); } } close(sock); } void emergency_shutdown() { close_sockets(1, (char *)"Going down - Bye"); } void bailout(sig, code, scp) int sig; int code; struct sigcontext *scp; { char message[128]; sprintf(message, "BAILOUT: caught signal %d code %d", sig, code); if((sig >= SIGINT) && (sig <= SIGSYS) && (sig != SIGKILL)) panic(message, 1); else panic(message, 0); exit(7); } void set_signals() { /* we don't care about SIGPIPE, we notice it in select() and write() */ signal(SIGPIPE, SIG_IGN); /* standard termination signals */ signal(SIGINT, bailout); signal(SIGTERM, bailout); /* things we need to core dump on, but want to trap */ /* signal(SIGQUIT, bailout); signal(SIGILL, bailout); signal(SIGTRAP, bailout); signal(SIGIOT, bailout); signal(SIGEMT, bailout); signal(SIGFPE, bailout); signal(SIGBUS, bailout); signal(SIGSEGV, bailout); signal(SIGSYS, bailout); */ } #ifdef LOCAL_RWHO_SERVER void dump_rusers(DESC *call_by) { struct sockaddr_in addr; struct hostent *hp; char *rbuf, *p, *srv; int fd; int red; if (!(mudconf.control_flags & CF_ALLOW_RWHO)) { queue_string(call_by, "RWHO is not available now.\r\n"); return; } p = srv = mudconf.rwho_host; while ((*p != '\0') && ((*p == '.') || isdigit(*p))) p++; if (*p != '\0') { if((hp = gethostbyname(srv)) == (struct hostent *)0) { queue_string(call_by,"Error connecting to rwho.\r\n"); return; } (void)bcopy(hp->h_addr,(char *)&addr.sin_addr,hp->h_length); } else { unsigned long f; if((f = inet_addr(srv)) == -1L) { queue_string(call_by,"Error connecting to rwho.\r\n"); return; } (void)bcopy((char *)&f,(char *)&addr.sin_addr,sizeof(f)); } addr.sin_port = htons(mudconf.rwho_data_port); addr.sin_family = AF_INET; close(mudstate.reserved_fileid); if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0) { queue_string(call_by, "Error in connecting to rwhod.\r\n"); return; } if(connect(fd,&addr,sizeof(addr)) < 0) { queue_string(call_by, "Error in connecting to rwhod.\r\n"); close(fd); mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0); return; } rbuf = alloc_lbuf("dump_rusers"); #ifndef VMS while((red = read(fd, rbuf, sizeof(rbuf))) > 0) { rbuf[red] = '\0'; strcat(rbuf,"\r\n"); queue_string(call_by, rbuf); } #else while((red = socket_read(fd, rbuf, sizeof(rbuf))) > 0) { rbuf[red] = '\0'; strcat(rbuf,"\r\n"); queue_string(call_by, rbuf); } #endif free_lbuf(rbuf); close(fd); mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0); } #endif /* LOCAL_RWHO_SERVER */