/* ************************************************************************ * file: idcomm.c , Communication module. Part of Copper3 * * Usage: Communication, central game loop. * * Copyright (C) 1990, 1991 - see 'license.doc' for complete information. * * All Rights Reserved * * Using *any* part of DikuMud without having read license.doc is * * violating our copyright. ************************************************************************* */ /* This is very much comm.c in new clothes... The idea is to multiplex on connections going to ident servers, so that slow/bogus servers can't keep new people from logging in. */ #include <errno.h> #include <ctype.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #include <netdb.h> #include CONFIG #if HAVE_STRINGS_H #include <strings.h> #endif #if HAVE_STRING_H #include <string.h> #endif #include <sys/time.h> #include <sys/timeb.h> #include <fcntl.h> #include <signal.h> #include "id.h" #ifndef sigmask #define dismask(m) (1 << ((m) - 1)) #endif /* For setrlimit */ #if CONFIG_SYSV #include <sys/resource.h> #endif #define MAX_NAME_LENGTH 15 #define MAX_HOSTNAME 256 #define OPT_USEC 250000 /* time delay corresponding to 4 passes/sec */ int maxdesc, avail_descs; int init_socket(int port); void close_sockets(int s); void close_socket(struct descriptor_data *d); struct timeval timediff(struct timeval *a, struct timeval *b); void nonblock(int s); /* Init sockets, handle requests, and cleanup sockets */ int ?????(int port) { int s; int retval; fd_set input_set, output_set, exc_set; struct timeval last_time, now, timespent, timeout, null_time; static struct timeval opt_time; char *comm; char buf[100]; #if CONFIG_SYSV struct rlimit limit = {100,105}; #endif null_time.tv_sec = 0; null_time.tv_usec = 0; opt_time.tv_usec = OPT_USEC; /* Init time values */ opt_time.tv_sec = 0; gettimeofday(&last_time, (struct timeval *) 0); maxdesc = s; #if CONFIG_BSD #if HAVe_SeTDTABLeSIZe retval = setdtablesize(100); if (retval == -1) log("unable to set table size"); else { sprintf(buf, "%s %d\n", "dtablesize set to: ", retval); log(buf); } #endif avail_descs = getdtablesize() - 8; /* !! Change if more needed !! */ #endif #if CONFIG_SYSV retval = setrlimit(RLIMIT_NOFILE,&limit); if (retval == -1) log("unable to set resource limit"); else { sprintf(buf, "%s %d\n", "rlimit size set to: ", retval); log(buf); } getrlimit(RLIMIT_NOFILE,&limit); avail_descs = limit.rlim_cur - 9; /* !! Change if more needed !! */ #endif sprintf(buf,"avail_descs set to: %d",avail_descs); log(buf); mask = sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGINT) | sigmask(SIGPIPE) | sigmask(SIGALRM) | sigmask(SIGTERM) | sigmask(SIGURG) | sigmask(SIGXCPU) | sigmask(SIGHUP); /* Main loop */ while (!shutdown) { /* Check what's happening out there */ FD_ZERO(&input_set); FD_ZERO(&output_set); FD_ZERO(&exc_set); /* FD_SET(s, &input_set); (incoming desc? */ FD_SET(0, &input_set); /* check for commands */ for (point = descriptor_list; point; point = point->next) { FD_SET(point->descriptor, &input_set); FD_SET(point->descriptor, &exc_set); FD_SET(point->descriptor, &output_set); } /* check out the time */ gettimeofday(&now, (struct timeval *) 0); timespent = timediff(&now, &last_time); timeout = timediff(&opt_time, ×pent); last_time.tv_sec = now.tv_sec + timeout.tv_sec; last_time.tv_usec = now.tv_usec + timeout.tv_usec; if (last_time.tv_usec >= 1000000) { last_time.tv_usec -= 1000000; last_time.tv_sec++; } sigsetmask(mask); if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) < 0) { perror("Select poll"); return; } if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) { perror("Select sleep"); exit(1); } sigsetmask(0); /* Respond to whatever might be happening */ if (FD_ISSET(0, &input_set)) read_new_req(); for (point = descriptor_list; point; point = next_point) { next_point = point->next; if (FD_ISSET(point->descriptor, &input_set)) { if (process_input(point) < 0) close_socket(point); } } /* kick out the freaky folks */ for (point = descriptor_list; point; point = next_point) { next_point = point->next; if (FD_ISSET(point->descriptor, &exc_set)) { FD_CLR(point->descriptor, &input_set); FD_CLR(point->descriptor, &output_set); close_socket(point); } } } } /* ****************************************************************** * general utility stuff (for local use) * ****************************************************************** */ struct timeval timediff(struct timeval *a, struct timeval *b) { struct timeval rslt, tmp; tmp = *a; if ((rslt.tv_usec = tmp.tv_usec - b->tv_usec) < 0) { rslt.tv_usec += 1000000; --(tmp.tv_sec); } if ((rslt.tv_sec = tmp.tv_sec - b->tv_sec) < 0) { rslt.tv_usec = 0; rslt.tv_sec =0; } return(rslt); } /* ****************************************************************** * socket handling * ****************************************************************** */ int init_socket(int port) { int s; char *opt; char hostname[MAX_HOSTNAME+1]; struct sockaddr_in sa; struct hostent *hp; struct linger ld; bzero(&sa, sizeof(struct sockaddr_in)); gethostname(hostname, MAX_HOSTNAME); hp = gethostbyname(hostname); if (hp == NULL) { perror("gethostbyname"); exit(1); } sa.sin_family = hp->h_addrtype; sa.sin_port = htons(port); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("Init-socket"); exit(1); } if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof (opt)) < 0) { perror ("setsockopt REUSEADDR"); exit (1); } ld.l_onoff = 0; ld.l_linger = 0; if (setsockopt(s, SOL_SOCKET, SO_LINGER, &ld, sizeof(ld)) < 0) { perror("setsockopt LINGER"); exit(1); } if (bind(s, &sa, sizeof(sa), 0) < 0) { perror("bind"); close(s); exit(1); } listen(s, 3); return(s); } int write_to_descriptor(int desc, char *txt) { int sofar, thisround, total; total = strlen(txt); sofar = 0; do { thisround = write(desc, txt + sofar, total - sofar); if (thisround < 0) { perror("Write to socket"); return(-1); } sofar += thisround; } while (sofar < total); return(0); } int process_input(struct descriptor_data *t) { int sofar, thisround, begin, squelch, i, k, flag; char tmp[MAX_INPUT_LENGTH+2], buffer[MAX_INPUT_LENGTH + 60]; sofar = 0; flag = 0; begin = strlen(t->buf); /* Read in some stuff */ do { if ((thisround = read(t->descriptor, t->buf + begin + sofar, MAX_STRING_LENGTH - (begin + sofar) - 1)) > 0) sofar += thisround; else if (thisround < 0) if(errno != EWOULDBLOCK) { perror("Read1 - ERROR"); return(-1); } else break; else { log("EOF encountered on socket read."); return(-1); } } while (!ISNEWL(*(t->buf + begin + sofar - 1))); *(t->buf + begin + sofar) = 0; /* if no newline is contained in input, return without proc'ing */ for (i = begin; !ISNEWL(*(t->buf + i)); i++) if (!*(t->buf + i)) return(0); /* input contains 1 or more newlines; process the stuff */ return(1); } void close_sockets(int s) { log("Closing all sockets."); while (descriptor_list) close_socket(descriptor_list); close(s); } void close_socket(struct descriptor_data *d) { struct descriptor_data *tmp; char buf[100]; close(d->descriptor); flush_queues(d); if (d->descriptor == maxdesc) --maxdesc; if (next_to_process == d) /* to avoid crashing the process loop */ next_to_process = next_to_process->next; if (d == descriptor_list) /* this is the head of the list */ descriptor_list = descriptor_list->next; else /* This is somewhere inside the list */ { /* Locate the previous element */ for (tmp = descriptor_list; (tmp->next != d) && tmp; tmp = tmp->next); tmp->next = d->next; } if (d->showstr_head) free(d->showstr_head); free(d); } void nonblock(int s) { #if CONFIG_SYSV if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) #else if (fcntl(s, F_SETFL, FNDELAY) == -1) #endif { perror("Noblock"); exit(1); } }