/* Concentrator upgraded June 1990 Robert Hood */ /* modifed Concentrator to match Fuzzy & Randoms 1.5.4 changes = 6/90 Fuzzy */ /* modified interface.c to support LOTS of people, using a concentrator */ /* May 1990, Robert Hood */ /* Warning: this file has not been upgraded to match all of the changes */ /* made to bsd.c. January 1992, Lydia Leong */ #include "copyright.h" #include <stdio.h> #include <varargs.h> #include <sys/param.h> #include <sys/types.h> #include <sys/file.h> #include <sys/time.h> #include <signal.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <fcntl.h> #include <sys/errno.h> #include <ctype.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <string.h> #include "config.h" #include "db.h" #include "interface.h" #include "externs.h" #include "globals.h" #ifdef MEM_CHECK #include "mem_check.h" #endif #define BUFSIZE 0xFFFF struct text_block { int nchars; struct text_block *nxt; char *start; char *buf; }; struct text_queue { struct text_block *head; struct text_block **tail; }; struct descriptor_data { int descriptor; int num; int connected; char *hostname; dbref player; char *output_prefix; char *output_suffix; int output_size; struct text_queue output; struct text_queue input; char *raw_input; char *raw_input_at; long connected_at; long last_time; int quota; #ifdef AT_DOING char doing[40]; #endif struct sockaddr_in address; /* added 3/6/90 SCG */ struct descriptor_data *next; }; struct message { char *data; short len; struct message *next; }; struct conc_list { struct conc_list *next; int sock, current, status; struct descriptor_data *firstd; struct message *first, *last; char *incoming, *outgoing; int ilen, olen; } *firstc = 0; void welcome_user(); void spew_message(); void writelog(); void queue_message(struct conc_list * c, char *data, int len); struct timeval timeval_sub(struct timeval now, struct timeval then); struct timeval msec_add(struct timeval t, int x); struct timeval update_quotas(struct timeval last, struct timeval current); void main_loop(); void raw_notify(dbref player, const char *msg); void process_output(struct conc_list * c); int process_input(struct descriptor_data * d, char *buf, int got); void process_commands(); void dump_users(struct descriptor_data *call_by, char *match); void free_text_block(struct text_block * t); void main(int argc, char **argv); void set_signals(); int msec_diff(struct timeval now, struct timeval then); void clearstrings(struct descriptor_data * d); void shutdownsock(struct descriptor_data * d); struct descriptor_data *initializesock(struct sockaddr_in * a); struct text_block *make_text_block(const char *s, int n); void add_to_queue(struct text_queue * q, const char *b, int n); int flush_queue(struct text_queue * q, int n); int queue_write(struct descriptor_data * d, const char *b, int n); int queue_string(struct descriptor_data * d, const char *s); void freeqs(struct descriptor_data * d); void welcome_user(struct descriptor_data * d); char *strsave(const char *s); void save_command(struct descriptor_data * d, const char *command); void set_userstring(char **userstring, const char *command); int do_command(struct descriptor_data * d, char *command); int check_connect(struct descriptor_data * d, const char *msg); void parse_connect(const char *msg, char *command, char *user, char *pass); void close_sockets(); void emergency_shutdown(void); void boot_off(dbref player); int bailout(int sig, int code, struct sigcontext * scp); char *time_format_1(long dt); char *time_format_2(long dt); void announce_connect(dbref); void announce_disconnect(dbref); int sigshutdown(int, int, struct sigcontext *); #ifdef RWHO_SEND #ifdef FULL_RWHO void dump_rusers(); #endif void rwho_update(); #endif static const char *connect_fail = "Either that player does not exist, or has a different password.\n"; #ifndef WCREAT static const char *create_fail = "Either there is already a player with that name, or that name is illegal.\n"; #endif static const char *flushed_message = "<Output Flushed>\n"; static const char *asterisk_line = "*****************************************************************"; int sock; int shutdown_flag = 0; extern dbref speaker; extern int errno; extern int reserved; extern dbref db_top; int login_allow = 1; char ccom[BUFFER_LEN]; dbref cplr; char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN], cf_nologinmotd_msg[BUFFER_LEN]; int port = TINYPORT; int intport = INTERNAL_PORT; void start_port() { int temp; struct sockaddr_in sin; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 1) { perror("socket"); exit(-1); } temp = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &temp, sizeof(temp)); sin.sin_family = AF_INET; sin.sin_port = htons(intport); sin.sin_addr.s_addr = htonl(INADDR_ANY); temp = bind(sock, (struct sockaddr *) & sin, sizeof(sin)); if (temp < 0) { perror("bind"); exit(-1); } temp = listen(sock, 5); if (temp < 0) { perror("listen"); exit(-1); } } 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; } 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; } struct timeval update_quotas(struct timeval last, struct timeval current) { int nslices; struct descriptor_data *d; struct conc_list *c; nslices = msec_diff(current, last) / COMMAND_TIME_MSEC; if (nslices > 0) { for (c = firstc; c; c = c->next) for (d = c->firstd; d; d = d->next) { d->quota += COMMANDS_PER_TIME * nslices; if (d->quota > COMMAND_BURST_SIZE) d->quota = COMMAND_BURST_SIZE; } } return msec_add(last, nslices * COMMAND_TIME_MSEC); } void raw_notify(dbref player, const char *msg) { struct descriptor_data *d; struct conc_list *c; #ifdef COMPRESS extern const char *uncompress(const char *); msg = uncompress(msg); #endif /* COMPRESS */ for (c = firstc; c; c = c->next) { for (d = c->firstd; d; d = d->next) { if (d->connected && d->player == player) { queue_string(d, msg); queue_write(d, "\n", 1); process_output(c); } } process_output(c); } } void raw_broadcast(va_alist) va_dcl { char buff[BUFFER_LEN]; va_list args; int inflags; char *template; struct descriptor_data *d; struct conc_list *c; va_start(args); inflags = va_arg(args, int); template = va_arg(args, char *); if(!template || !*template) return; vsprintf(buff, template, args); for (c = firstc; c; c = c->next) { for (d = c->firstd; d; d = d->next) { if (d->connected && (db[d->player].flags & inflags) == inflags) { queue_string(d, buff); queue_write(d, "\n", 1); process_output(c); } } process_output(c); } } int process_input(d, buf, got) struct descriptor_data *d; char *buf; int got; { char *p, *pend, *q, *qend; d->last_time = time(0); if (!d->raw_input) { d->raw_input = (char *) malloc(sizeof(char) * MAX_COMMAND_LEN); if(!d->raw_input) panic("Out of memory"); #ifdef MEM_CHECK add_check("descriptor_raw_input"); #endif 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; q < qend; q++) { if (*q == '\n') { *p = '\0'; if (p > d->raw_input) save_command(d, d->raw_input); p = d->raw_input; } else if (p < pend && isascii(*q) && isprint(*q)) { *p++ = *q; } } if (p > d->raw_input) { d->raw_input_at = p; } else { free((void *) d->raw_input); #ifdef MEM_CHECK del_check("descriptor_raw_input"); #endif d->raw_input = 0; d->raw_input_at = 0; } return 1; } void process_commands() { int nprocessed; struct descriptor_data *d, *dnext, *dlast; struct conc_list *c; struct text_block *t; char header[4]; do { nprocessed = 0; for (c = firstc; c; c = c->next) { dlast = 0; for (d = c->firstd; d; d = dnext) { dnext = d->next; if (d->quota > 0 && (t = d->input.head)) { d->quota--; nprocessed++; if (!do_command(d, t->start)) { shutdownsock(d); process_output(c); header[0] = 0; header[1] = 2; header[2] = d->num; queue_message(c, header, 3); process_output(c); if (dlast) dlast->next = dnext; else c->firstd = dnext; free((void *)d); break; } else { d->input.head = t->nxt; if (!d->input.head) d->input.tail = &d->input.head; free_text_block(t); } } dlast = d; } } } while (nprocessed > 0); } void dump_users(struct descriptor_data *call_by, char *match) { struct descriptor_data *d; struct conc_list *c; time_t now; int count = 0; char tbuf1[BUFFER_LEN]; while (*match && isspace(*match)) match++; if (!*match) match = NULL; time(&now); if(Wizard(call_by->player)) { #ifdef AT_DOING queue_string(call_by, "Player Name Location On For Idle [Host ] / Doing\n"); #else queue_string(call_by, "Player Name Location On For Idle [Host]\n"); #endif } else { #ifdef AT_DOING queue_string(call_by, "Player Name On For Idle Doing\n"); #else queue_string(call_by, "Player Name On For Idle\n"); #endif } for (c = firstc; c; c = c->next) { for (d = c->firstd; d; d = d->next) { if (d->connected) { if(d->player < 0 || d->player >= db_top) continue; if(!Dark(d->player) || Wizard(call_by->player)) ++count; if(match && !(string_prefix(db[d->player].name, match))) continue; if(call_by->connected && Wizard(call_by->player)) { sprintf(tbuf1, "%-16s [%6d] %9s %5s [%s]", db[d->player].name, getloc(d->player), time_format_1(now - d->connected_at), time_format_2(now - d->last_time), d->hostname); if(Dark(d->player)) sprintf(tbuf1+strlen(tbuf1)," (Dark)"); } else { if(!Dark(d->player)) { #ifdef AT_DOING sprintf(tbuf1, "%-16s %10s %4s %s", #else sprintf(tbuf1, "%-16s %10s %4s", #endif db[d->player].name, time_format_1(now - d->connected_at), time_format_2(now - d->last_time) #ifdef AT_DOING , d->doing #endif ); } } if(!Dark(d->player) || Wizard(call_by->player)) { queue_string(call_by, tbuf1); queue_write(call_by, "\n", 1); } #ifdef AT_DOING if(Wizard(call_by->player) && d->doing[0]) { sprintf(tbuf1, "%46s %s", " ", d->doing); queue_string(call_by, tbuf1); queue_write(call_by, "\n", 1); } #endif } } } sprintf(tbuf1, "There %s %d user%s connected\n", (count == 1 ? "is" : "are"), count, (count == 1 ? "" : "s")); queue_string(call_by, tbuf1); } void free_text_block(struct text_block * t) { if (t) { if (t->buf) free((void *) t->buf); free((void *) t); } #ifdef MEM_CHECK del_check("text_block"); del_check("text_block_buff"); #endif } #ifndef BOOLEXP_DEBUGGING void main(int argc, char **argv) { int pid; const char *def_db_in = DEF_DB_IN; const char *def_db_out = DEF_DB_OUT; if(argc > 1) { --argc; def_db_in = *++argv; } if(argc > 1) { --argc; def_db_out = *++argv; } if(argc > 1) { --argc; port = atoi(*++argv); } if(argc > 1) { --argc; intport = atoi(*++argv); } srand(time(NULL)); reserved = open("/dev/null", O_RDWR); if (init_game(def_db_in, def_db_out) < 0) { writelog("ERROR: Couldn't load %s! Exiting.\n", def_db_in); exit(2); } pid = vfork(); if (pid < 0) { perror("fork"); exit(-1); } if (pid == 0) { char pstr[32], istr[32], clvl[32]; /* Add port argument to concentrator */ sprintf(pstr, "%d", port); sprintf(istr, "%d", intport); sprintf(clvl, "%d", 1); execl("concentrate", "conc", pstr, istr, clvl, 0); } set_signals(); start_port(port); main_loop(); close_sockets(); dump_database(); exit(0); } #endif void set_signals(void) { signal(SIGPIPE, SIG_IGN); signal(SIGINT, (void *) bailout); signal(SIGTERM, (void *) bailout); } int msec_diff(struct timeval now, struct timeval then) { return ((now.tv_sec - then.tv_sec) * 1000 + (now.tv_usec - then.tv_usec) / 1000); } void clearstrings(struct descriptor_data * d) { if (d->output_prefix) { free((void *)d->output_prefix); #ifdef MEM_CHECK del_check("userstring"); #endif d->output_prefix = 0; } if (d->output_suffix) { free((void *)d->output_suffix); #ifdef MEM_CHECK del_check("userstring"); #endif d->output_suffix = 0; } } void shutdownsock(struct descriptor_data *d) { struct conc_list *c; struct descriptor_data *d1; if (d->connected) { spew_message(d, LEAVE_MSG_FILE); writelog("DISCONNECT descriptor %d,%d player %s(%d)\n", d->descriptor, d->num, db[d->player].name, d->player); announce_disconnect(d->player); } else { writelog("DISCONNECT descriptor %d,%d never connected\n", d->descriptor, d->num); } for(c = firstc; c; c = c->next) { for(d1 = c->firstd; d1; d1 = d1->next) { process_output(c); } } clearstrings(d); freeqs(d); } struct descriptor_data * initializesock(struct sockaddr_in * a) { struct descriptor_data *d; d = (struct descriptor_data *) malloc(sizeof(struct descriptor_data)); if(!d) panic("Out of memory."); #ifdef MEM_CHECK add_check("descriptor"); #endif d->connected = 0; d->player = 0; d->output_prefix = 0; d->output_suffix = 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->address = *a; /* This will be the address of the * concentrator */ d->hostname = ""; /* This will be set during connect */ #ifdef AT_DOING d->doing[0] = '\0'; #endif welcome_user(d); return d; } struct text_block * make_text_block(const char *s, int n) { struct text_block *p; p = (struct text_block *) malloc(sizeof(struct text_block)); if(!p) panic("Out of memory"); p->buf = (char *) malloc(sizeof(char) * n); if(!p->buf) panic("Out of memory"); #ifdef MEM_CHECK add_check("text_block"); add_check("text_block_buff"); #endif bcopy(s, p->buf, n); p->nchars = n; p->start = p->buf; p->nxt = 0; return p; } void add_to_queue(struct text_queue * q, const char *b, int n) { struct text_block *p; if (n == 0) return; p = make_text_block(b, n); p->nxt = 0; *q->tail = p; q->tail = &p->nxt; } int flush_queue(struct text_queue * q, int n) { struct text_block *p; int 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)); p->nxt = q->head; q->head = p; if (!p->nxt) q->tail = &p->nxt; really_flushed -= p->nchars; return really_flushed; } int queue_write(struct descriptor_data * d, const char *b, int n) { int space; space = MAX_OUTPUT - d->output_size - n; if (space < 0) d->output_size -= flush_queue(&d->output, -space); add_to_queue(&d->output, b, n); d->output_size += n; return n; } int queue_string(struct descriptor_data * d, const char *s) { return queue_write(d, s, strlen(s)); } void freeqs(struct descriptor_data * d) { struct text_block *cur, *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((void *) d->raw_input); #ifdef MEM_CHECK del_check("descriptor_raw_input"); #endif } d->raw_input = 0; d->raw_input_at = 0; } void welcome_user(struct descriptor_data * d) { spew_message(d, WELCOME_MSG_FILE); } void spew_message(struct descriptor_data *d, char *filename) { int n, fd; char buf[512]; close(reserved); if ((fd = open(filename, O_RDONLY)) != -1) { while ((n = read(fd, buf, 512)) > 0) queue_write(d, buf, n); close(fd); queue_write(d, "\n", 1); } reserved = open("/dev/null", O_RDWR); } char * strsave(const char *s) { char *p; p = (char *) malloc(sizeof(char) * (strlen(s)+1)); if(!p) panic("Out of memory"); #ifdef MEM_CHECK add_check("userstring"); #endif if (p) strcpy(p, s); return p; } void save_command(struct descriptor_data * d, const char *command) { add_to_queue(&d->input, command, strlen(command) + 1); } void set_userstring(char **userstring, const char *command) { if (*userstring) { free((void *) *userstring); #ifdef MEM_CHECK del_check("userstring"); #endif *userstring = 0; } while (*command && isascii(*command) && isspace(*command)) command++; if (*command) *userstring = strsave(command); } int do_command(struct descriptor_data * d, char *command) { depth = 0; if (!strcmp(command, QUIT_COMMAND)) { return 0; } else if (!strncmp(command, WHO_COMMAND, strlen(WHO_COMMAND))) { if (d->output_prefix) { queue_string(d, d->output_prefix); queue_write(d, "\n", 1); } dump_users(d, command + strlen(WHO_COMMAND)); if (d->output_suffix) { queue_string(d, d->output_suffix); queue_write(d, "\n", 1); } } else if (d->connected && !strncmp(command, PREFIX_COMMAND, strlen(PREFIX_COMMAND))) { set_userstring(&d->output_prefix, command + strlen(PREFIX_COMMAND)); } else if (d->connected && !strncmp(command, SUFFIX_COMMAND, strlen(SUFFIX_COMMAND))) { set_userstring(&d->output_suffix, command + strlen(SUFFIX_COMMAND)); #ifdef RWHO_SEND #ifdef FULL_RWHO } else if (!strcmp(command, RWHO_COMMAND)) { dump_rusers(d); #endif #endif } else { if (d->connected) { if (d->output_prefix) { queue_string(d, d->output_prefix); queue_write(d, "\n", 1); } cplr = d->player; strcpy(ccom, command); process_command(d->player, command, d->player); if (d->output_suffix) { queue_string(d, d->output_suffix); queue_write(d, "\n", 1); } } else { if (!check_connect(d, command)) return 0; } } return 1; } int check_connect(struct descriptor_data * d, const char *msg) { char command[MAX_COMMAND_LEN]; char user[MAX_COMMAND_LEN]; char password[MAX_COMMAND_LEN]; dbref player; parse_connect(msg, command, user, password); if (!strncmp(command, "co", 2)) { player = connect_player(user, password); if (player == NOTHING) { queue_string(d, connect_fail); writelog("FAILED CONNECT %s on descriptor %d,%d\n", user, d->descriptor, d->num); } else { writelog("CONNECTED %s(%d) on descriptor %d,%d %s\n", db[player].name, player, d->descriptor, d->num, d->hostname); #ifdef AT_DOING d->doing[0] = '\0'; #endif d->connected = 1; d->connected_at = time((time_t *) 0); d->player = player; if(!login_allow && !Wizard(player)) { spew_message(d, DISABLE_MSG_FILE); raw_notify(player, asterisk_line); if(cf_nologinmotd_msg && *cf_nologinmotd_msg) raw_notify(player, cf_nologinmotd_msg); raw_notify(player, asterisk_line); return 0; } spew_message(d, CONNECT_MSG_FILE); if(Wizard(player)) { spew_message(d, WIZARD_MSG_FILE); } /* set the Lastsite attribute */ atr_add(player, "LASTSITE", d -> addr, GOD, NOTHING); announce_connect(player); do_look_around(player); /* give wizards their message, too - d'mike 7/15/91 */ if (db[player].flags & HAVEN) { notify(player, "Your HAVEN flag is set. You cannot receive pages."); } } } else if (!strncmp(command, "cr", 2)) { #ifndef WCREAT player = create_player(user, password); if (player == NOTHING) { queue_string(d, create_fail); writelog("FAILED CREATE %s on descriptor %d,%d %s\n", user, d->descriptor, d->num, d->hostname); } else { writelog("CREATED %s(%d) on descriptor %d,%d %s\n", db[player].name, player, d->descriptor, d->num, d->hostname); #ifdef AT_DOING d->doing[0] = '\0'; #endif d->connected = 1; d->connected_at = time((time_t *) 0); d->player = player; if(!login_allow) { spew_message(d, DISABLE_MSG_FILE); raw_notify(player, asterisk_line); if(cf_nologinmotd_msg && *cf_nologinmotd_msg) raw_notify(player, cf_nologinmotd_msg); raw_notify(player, asterisk_line); return 0; } /* give new players a special message */ spew_message(d, NEW_CONNECT_MSG_FILE); /* set the Lastsite attribute */ atr_add(player, "LASTSITE", d -> addr, GOD, NOTHING); announce_connect(player); do_look_around(player); } #else spew_message(d, REGISTER_MSG_FILE); #endif /* WCREAT */ } else { welcome_user(d); } return 1; } void parse_connect(const char *msg, char *command, char *user, char *pass) { char *p; while (*msg && isascii(*msg) && isspace(*msg)) msg++; p = command; while (*msg && isascii(*msg) && !isspace(*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii(*msg) && isspace(*msg)) msg++; p = user; while (*msg && isascii(*msg) && !isspace(*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii(*msg) && isspace(*msg)) msg++; p = pass; while (*msg && isascii(*msg) && !isspace(*msg)) *p++ = *msg++; *p = '\0'; } void close_sockets(void) { struct conc_list *c; #ifdef RWHO_SEND rwhocli_shutdown(); #endif for (c = firstc; c; c = c->next) { /* conc.c now handles printing the Going Down - Bye message */ shutdown(c->sock, 0); close(c->sock); } close(sock); } void emergency_shutdown(void) { close_sockets(); } int bailout(int sig, int code, struct sigcontext *scp) { int i; writelog("BAILOUT: caught signal %d code %d", sig, code); panic("PANIC on spurious signal"); _exit(7); return 0; } char *time_format_1(long dt) { register struct tm *delta; static char buf[64]; if(dt < 0) dt = 0; delta = gmtime(&dt); if (delta->tm_yday > 0) { sprintf(buf, "%dd %02d:%02d", delta->tm_yday, delta->tm_hour, delta->tm_min); } else { sprintf(buf, "%02d:%02d", delta->tm_hour, delta->tm_min); } return buf; } char * time_format_2(long dt) { register struct tm *delta; static char buf[64]; if(dt < 0) dt = 0; delta = gmtime(&dt); if (delta->tm_yday > 0) { sprintf(buf, "%dd", delta->tm_yday); } else if (delta->tm_hour > 0) { sprintf(buf, "%dh", delta->tm_hour); } else if (delta->tm_min > 0) { sprintf(buf, "%dm", delta->tm_min); } else { sprintf(buf, "%ds", delta->tm_sec); } return buf; } void announce_connect(dbref player) { dbref loc; ATTR *temp; char buf[BUFFER_LEN]; db[player].flags |= PLAYER_CONNECT; if (db[player].flags & PLAYER_SUSPECT) raw_broadcast(WIZARD, "Broadcast: Suspect %s has connected.", db[player].name); if ((loc = getloc(player)) == NOTHING) { notify(player, "You are nowhere!"); return; } speaker = player; #ifdef RWHO_SEND sprintf(buf,"%d@%s", player, MUDNAME); rwhocli_userlogin(buf, db[player].name, time((time_t *) 0)); #endif raw_notify(player, asterisk_line); if(cf_motd_msg && *cf_motd_msg) raw_notify(player, cf_motd_msg); raw_notify(player, " "); if(Wizard(player) && cf_wizmotd_msg && *cf_wizmotd_msg) raw_notify(player, cf_wizmotd_msg); raw_notify(player, asterisk_line); sprintf(buf, "%s has connected.", db[player].name); notify_except(db[player].contents, player, buf); /* added to allow player's inventory to hear a player connect */ if(!Dark(player)) notify_except(db[loc].contents, player, buf); temp = atr_get(player, "ACONNECT"); if (temp) { char *s = safe_uncompress(temp->value); parse_que(player, s, player); free(s); } } void announce_disconnect(dbref player) { dbref loc; int num; ATTR *temp; struct descriptor_data *d; struct conc_list *c; char tbuf1[BUFFER_LEN]; if ((loc = getloc(player)) == NOTHING) return; speaker = player; for (num = 0, c = firstc; c; c = c->next) for (d = c->firstd; d; d = d->next) if (d->connected && (d->player == player)) num++; if (num < 2) { #ifdef RWHO_SEND sprintf(tbuf1, "%d@%s", player, MUDNAME); rwhocli_userlogout(tbuf1); #endif sprintf(tbuf1, "%s has disconnected.", db[player].name); if(!Dark(player)) notify_except(db[loc].contents, player, tbuf1); /* notify contents */ notify_except(db[player].contents,player, tbuf1); temp = atr_get(player, "ADISCONNECT"); if (temp) { char *s = safe_uncompress(temp->value); parse_que(player, s, player); free(s); } db[player].flags &= ~PLAYER_CONNECT; if (db[player].flags & PLAYER_SUSPECT) raw_broadcast(WIZARD, "Broadcast: Suspect %s has disconnected.", db[player].name); } } #ifdef AT_DOING extern char *reconstruct_message(); void do_doing(player, arg1, arg2) dbref player; char *arg1, *arg2; { char *message, buf[MAX_COMMAND_LEN]; struct descriptor_data *d; message = reconstruct_message(arg1, arg2); sprintf(buf, message); buf[39]='\0'; for (d = descriptor_list; d; d=d->next) if (d->connected && (d->player == player)) strcpy(d->doing,buf); notify(player, "Set."); } #endif #ifdef RWHO_SEND #ifdef FULL_RWHO void dump_rusers(call_by) struct descriptor_data *call_by; { struct sockaddr_in addr; struct hostent *hp; char *p; int fd; int red; char *srv = NULL; int portnum = RWHOPORT; char tbuf1[BUFFER_LEN]; p = srv = (char *)RWHOSERV; while (*p != '\0' && (*p == '.' || isdigit(*p))) p++; if(*p != '\0') { if((hp = gethostbyname(srv)) == (struct hostent *)0) { fprintf(stderr,"ERROR: unknown host %s\n",srv); queue_string(call_by,"Error in connecting to the RWHO server.\n"); return; } (void)bcopy(hp->h_addr,(char *)&addr.sin_addr,hp->h_length); } else { unsigned long f; if((f = inet_addr(srv)) == -1L) { fprintf(stderr,"ERROR: unknown host %s\n",srv); queue_string(call_by,"Error in connecting to the RWHO server.\n"); return; } (void)bcopy((char *)&f,(char *)&addr.sin_addr,sizeof(f)); } addr.sin_port = htons(portnum); addr.sin_family = AF_INET; if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0) { queue_string(call_by, "Socket error in connecting to rwhod. sorry.\n"); return; } if(connect(fd,&addr,sizeof(addr)) < 0) { queue_string(call_by, "Connect error in connecting to rwhod. sorry.\n"); return; } while((red = read(fd, tbuf1, sizeof(tbuf1))) > 0) queue_write(call_by, tbuf1, red); close(fd); } #endif /*FULL_RWHO*/ void rwho_update() { struct descriptor_data *d; struct conc_list *c; char tbuf1[BUFFER_LEN]; rwhocli_pingalive(); for (c = firstc; c; c = c->next) { for (d = c->firstd; d; d = d->next) { if (d->connected && !Dark(d->player)) { sprintf(tbuf1, "%d@%s", d->player, MUDNAME); rwhocli_userlogin(tbuf1, db[d->player].name, d->connected_at); } } } } #endif RWHO_SEND #ifdef LOCKOUT int quick_wild(s, d) char *s; char *d; { switch(*s) { case '?': return(wild(s+1, (*d) ? d+1 : d)); case '*': return(wild(s+1, d) || ((*d) ? wild(s,d+1) : 0)); default: return((UPCASE(*s) != UPCASE(*d)) ? 0 : ((*s) ? wild(s+1,d+1) : 1)); } } int forbidden_site(hname) const char *hname; { char buf[MAXHOSTNAMELEN], *newlin; FILE *fp; fp = fopen(LOCKOUT_FILE, "r"); while ((fp != NULL) && (!feof(fp))) { fgets(buf, MAXHOSTNAMELEN, fp); /* step on the newline */ if ((newlin = index(buf, '\n')) != NULL) *newlin = '\0'; if (!strcasecmp(hname, buf)) { fclose(fp); return 1; } } fclose(fp); return 0; } const char *addrout(a) long a; { static char buf[MAXHOSTNAMELEN]; struct hostent *he; he = gethostbyaddr(&a, sizeof(a), AF_INET); if (he) { return he->h_name; } else { a = ntohl(a); sprintf (buf, "%d.%d.%d.%d", (a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff); return buf; } } #endif /* LOCKOUT */ dbref short_page(match) const char *match; { struct descriptor_data *d; struct conc_list *c; dbref who1 = NOTHING; int count = 0; for(c = firstc; c; c = c->next) { for(d = c->firstd; d; d = d->next) { if(d->connected) { if(match && !string_prefix(db[d->player].name, match)) continue; if(!string_compare(db[d->player].name, match)) { count = 1; who1 = d->player; break; } who1 = d->player; count++; } } } if(count > 1) return AMBIGUOUS; else if (count == 0) return NOTHING; return who1; } void main_loop() { struct message *ptr; int found, newsock, lastsock, len; int accepting = 1; struct timeval tv; struct sockaddr_in sin; fd_set in, out; char buf[BUFFER_LEN]; struct conc_list *c, *tempc, *nextc, *lastc; struct descriptor_data *d, *tempd; struct timeval last_slice, current_time; struct timeval next_slice; struct timeval slice_timeout, timeout; short templen; time_t tt; extern void dispatch(); lastsock = sock + 1; while (!shutdown_flag) { gettimeofday(¤t_time, (struct timezone *) 0); last_slice = update_quotas(last_slice, current_time); process_commands(); if (shutdown_flag) break; dispatch(); timeout.tv_sec = test_top() ? 0 : 1000; timeout.tv_usec = 0; next_slice = msec_add(last_slice, COMMAND_TIME_MSEC); slice_timeout = timeval_sub(next_slice, current_time); FD_ZERO(&in); FD_ZERO(&out); FD_SET(sock, &in); for (c = firstc; c; c = c->next) process_output(c); for (c = firstc; c; c = c->next) if (c->sock) { if (c->ilen < BUFSIZE) FD_SET(c->sock, &in); len = c->first ? c->first->len : 0; while (c->first && ((c->olen + len + 2) < BUFSIZE)) { templen = c->first->len; bcopy(&templen, c->outgoing + c->olen, 2); bcopy(c->first->data, c->outgoing + c->olen + 2, len); c->olen += len + 2; ptr = c->first; c->first = ptr->next; free((void *)ptr->data); free((void *)ptr); if (c->last == ptr) c->last = 0; len = c->first ? c->first->len : 0; } if (c->olen) FD_SET(c->sock, &out); } else timeout = slice_timeout; if((found = select(lastsock, &in, &out, (fd_set *) 0, &timeout)) < 0) { if(errno != EINTR) { perror("select"); return; } } else { if(!found) { do_top(); do_top(); do_top(); continue; } if (accepting && FD_ISSET(sock, &in)) { len = sizeof(sin); newsock = accept(sock, (struct sockaddr *) & sin, &len); if (newsock >= 0) { if (newsock >= lastsock) lastsock = newsock + 1; if (fcntl(newsock, F_SETFL, FNDELAY) == -1) { perror("make_nonblocking: fcntl"); } tempc = (struct conc_list *)malloc(sizeof(struct conc_list)); if(!tempc) panic("Out of memory."); tempc->next = firstc; tempc->firstd = 0; tempc->first = 0; tempc->last = 0; tempc->status = 0; /* Imcomming and outgoing I/O buffers */ tempc->incoming = (char *)malloc(BUFSIZE); if(!tempc->incoming) panic("Out of memory."); tempc->ilen = 0; tempc->outgoing = (char *)malloc(BUFSIZE); if(!tempc) panic("Out of memory."); tempc->olen = 0; firstc = tempc; firstc->sock = newsock; writelog("CONCENTRATOR CONNECT: sock %d, addr %x\n", newsock, sin.sin_addr.s_addr); } } for (c = firstc; c; c = nextc) { nextc = c->next; #ifdef CHECKC if (!(c->sock)) writelog("CONSISTENCY CHECK: Concentrator found with null socket #\n"); #endif if ((FD_ISSET(c->sock, &in)) && (c->ilen < BUFSIZE)) { len = recv(c->sock, c->incoming + c->ilen, BUFSIZE - c->ilen, 0); if (len == 0) { struct message *mptr, *tempm; writelog("CONCENTRATOR DISCONNECT: %d\n", c->sock); close(c->sock); d = c->firstd; while (d) { shutdownsock(d); tempd = d; d = d->next; free((void *)tempd); } if (firstc == c) firstc = firstc->next; else lastc->next = c->next; free((void *)c->incoming); free((void *)c->outgoing); mptr = c->first; while (mptr) { tempm = mptr; mptr = mptr->next; free((void *)mptr->data); free((void *)mptr); } free((void *)c); break; } else if (len < 0) { writelog("recv: %s\n", strerror(errno)); } else { int num; c->ilen += len; while (c->ilen > 2) { bcopy(c->incoming, &templen, 2); #ifdef CHECKC if (templen < 1) writelog("CONSISTENCY CHECK: Message recived with length < 1\n"); #endif if (c->ilen >= (templen + 2)) { num = *(c->incoming + 2); /* Is it coming from the command user #? */ if (num == 0) { /* Proccess commands */ switch (*(c->incoming + 3)) { case 1: /* connect */ tt = time((time_t *) 0); d = initializesock(&sin); d->descriptor = c->sock; d->next = c->firstd; c->firstd = d; d->num = *(c->incoming + 4); d->hostname= (char *)malloc(templen - 5); if(!d->hostname) panic("Out of memory."); bcopy(c->incoming + 9, d->hostname, templen - 6); *(d->hostname + templen - 7) = 0; writelog("USER CONNECT %d,%d from host %s at %s\n", c->sock, d->num, d->hostname, ctime(&tt)); break; case 2: /* disconnect */ tempd = 0; d = c->firstd; num = *(c->incoming + 4); while (d) { if (d->num == num) { writelog("USER ABORTED CONNECTION %d,%d\n", c->sock, d->num); shutdownsock(d); if (c->firstd == d) c->firstd = d->next; else tempd->next = d->next; free((void *)d); break; } tempd = d; d = d->next; } #ifdef CHECKC if (!d) writelog("CONSISTENCY CHECK: Disconnect Received for unknown user %d,%d\n", c->sock, num); #endif break; /* * This take a message from a concentrator, and logs it * in the log file */ case 4: { bcopy(c->incoming + 4, buf, templen - 2); *(buf + templen - 1) = 0; writelog(buf); break; } #ifdef CHECKC default: writelog("CONSISTENCY CHECK: Received unknown command from concentrator\n"); #endif } } else { d = c->firstd; while (d) { if (d->num == num) { process_input(d, c->incoming + 3, templen - 1); break; } d = d->next; } #ifdef CHECKC if (!d) writelog("CONSISTENCY CHECK: Message received for unknown user %d,%d\n", c->sock, num); #endif } bcopy(c->incoming + templen + 2, c->incoming, (c->ilen - templen - 2)); c->ilen = c->ilen - templen - 2; } else break; } } } lastc = c; } } /* Send data loop */ for (c = firstc; c; c = c->next) { if (FD_ISSET(c->sock, &out)) { if (c->olen) { len = send(c->sock, c->outgoing, c->olen, 0); if (len > 0) { c->olen -= len; bcopy(c->outgoing + len, c->outgoing, c->olen); } } } } } } void process_output(struct conc_list * c) { struct descriptor_data *d; struct text_block **qp, *cur; short templen; for (d = c->firstd; d; d = d->next) { qp = &d->output.head; cur = *qp; if (cur) { if (cur->nchars < 512) { if ((c->olen + cur->nchars + 3) < BUFSIZE) { templen = cur->nchars + 1; bcopy(&templen, c->outgoing + c->olen, 2); *(c->outgoing + c->olen + 2) = d->num; strncpy(c->outgoing + c->olen + 3, cur->start, cur->nchars); d->output_size -= cur->nchars; c->olen += cur->nchars + 3; if (!cur->nxt) d->output.tail = qp; *qp = cur->nxt; free_text_block(cur); } } else { if ((c->olen + 512 + 3) < BUFSIZE) { templen = 512 + 1; bcopy(&templen, c->outgoing + c->olen, 2); *(c->outgoing + c->olen + 2) = d->num; strncpy(c->outgoing + c->olen + 3, cur->start, 512); d->output_size -= 512; c->olen += 512 + 3; cur->nchars -= 512; cur->start += 512; } } } } } void boot_off(dbref player) { struct conc_list *c; struct descriptor_data *d, *lastd; char header[4]; for (c = firstc; c; c = c->next) { lastd = 0; for (d = c->firstd; d; d = d->next) { if (d->connected && d->player == player) { shutdownsock(d); header[0] = 0; header[1] = 2; header[2] = d->num; queue_message(c, header, 3); process_output(c); if (lastd) lastd->next = d->next; else c->firstd = d->next; free((void *) d); return; } lastd = d; } } } void queue_message(struct conc_list * c, char *data, int len) { struct message *ptr; ptr = (struct message *) malloc(sizeof(struct message)); if(!ptr) panic("Out of memory."); ptr->data = (char *) malloc(len); if(!ptr->data) panic("Out of memory."); ptr->len = len; bcopy(data, ptr->data, len); ptr->next = 0; if (c->last == 0) c->first = ptr; else c->last->next = ptr; c->last = ptr; } void writelog(va_alist) va_dcl { char buf[BUFFER_LEN]; va_list args; char *fmt; va_start(args); fmt = va_arg(args, char *); (void) vsprintf(buf, fmt, args); buf[BUFFER_LEN -1] = '\0'; fprintf(stderr, buf); }