Update of /cvsroot/netrek/metaserver In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30299 Added Files: BecomeDaemon.c Makefile PROJECTS checkalive disp_info.c disp_new.c disp_old.c disp_udp.c disp_web.c features goaway.txt main.c meta.h metaII_info metacheck.c metalist metarc packets.h rsa_keys scan.c server.c server_faq serverlist Log Message: Importing the metaserver into the sourceforge CVS. Unfortunately, some revision history was lost from the old Tanner server. This version is the active version as of 2006-Feb-13. --Carlos V. --- NEW FILE: goaway.txt --- Go away. --- NEW FILE: features --- # # Syntax: # FEATURE_NAME <S/C> <ON/OFF> [int-arg] [int-arg] # # Description: # FEATURE_NAME: is a unique string of up to 80 characters describing # a client or server feature. # <S/C>: 'S' indicates that the feature requires server support. Servers # that do not support the feature, regardless of how it is listed in the # .features file, will return value UNKNOWN to clients requesting that # feature. 'C' indicates that the feature is a client option, requiring no # special code support from the server. # <ON/OFF>: As stated above, if the server does not support a given feature, # this value is ignored and UNKNOWN will be sent to the client. Otherwise # this value is returned and will be honored by the client. # [int-args]: two optional integer args may be used for future extensions. # # feature packets support FEATURE_PACKETS S ON # why-dead message extra args (see vanilla server) WHY_DEAD S ON # receiver-configurable distress (see vanilla server) RC_DISTRESS S ON # multi-line macros (see vanilla server) MULTIMACROS C ON # first 8 flags of my ship sent in SP_S_YOU_SS (see vanilla server) SELF_8FLAGS S ON # warp 15 means player is cloaked (see vanilla server) CLOAK_MAXWARP S ON # SB hours (?) SBHOURS S ON # client macro switch (old clients) NEWMACRO C ON # client macro switch (old clients) SMARTMACRO C ON # continuous mouse CONTINUOUS_MOUSE C OFF # continuous steering CONTINUOUS_STEER C ON # info mode INFO_MODE1 C ON # beeplite options. These get or'ed together to produce the BEEPLITE value. # LITE_PLAYERS_MAP 1 # LITE_PLAYERS_LOCAL 2 # LITE_SELF 4 # LITE_PLANETS 8 # LITE_SOUNDS 16 # LITE_COLOR 32 # LITE_TTS 64 # BEEPLITE C ON 95 # lite players map LITE_PLAYERS_MAP C ON # lite players local LITE_PLAYERS_LOCAL C ON # lite self LITE_SELF C ON # lite planets LITE_PLANETS C ON # lite sounds LITE_SOUNDS C ON # ship stat. packets SHIP_CAP S ON # # # OLD FORMAT FOR CLIENTS WITHOUT FEATURE_PACKETS SUPPORT. # # INL SERVERS READ: The inl servers are using a broken version of # feature.c and no one seems to want to fix it. So ALL the # features without confirm strings WILL NOT work. YOU WILL # HAVE TO PUT A CONFIRM ON THEM. To do this for example change: # Client Of Win: WHY_DEAD to # Client Of Win*confirm:0.00.0: WHY_DEAD # # # # BRM FEATURES # # NEWMACRO and SMARTMACRO are intelligent message macro sending. For more info # look at the NEWMACRO.DOC that comes with the BRM source. # These should be disabled soon since they are now on by default and BRM2.99 # doesn't use these anymore. BerkRick Moo!confirm:2.99.0: NEWMACRO BerkRick Moo!confirm:2.99.0: SMARTMACRO # # WHY_DEAD will allow the server/client to communicate why a player died while # using Short Packets. BerkRick Moo*confirm:2.00.15: WHY_DEAD # ricksmoo 3.0*confirm:moo 3.0.99: WHY_DEAD # # # Starting with 2.99 the BRM will have SMART_MACROS on unless told not to by # the server. NOSMARTMACROS turns off smart-macros. #BerkRick Moo*confirm:2.99.0: NO_SMARTMACROS # # NO_SMARTDISTRESS is new to BRM 2.99. It disables the smart distresses when # the client recieves the string. #BerkRick Moo*confirm:2.99.0: NO_SMARTDISTRESS # # RC_DISTRESS is new to BRM 2.99pl2. It enables variable distress calls. If the # server has it then it should be enabled. BerkRick Moo*confirm:2.99.3: RC_DISTRESS # # NO_CONTINUOUS_MOUSE is to disable the ability to have auto-repeat with the # mouse. BerkRick Moo*confirm:3.00.0: NO_CONTINUOUS_MOUSE # # # BRM INFO CLIENT FEATURES # # Disable all of BRMi's enhancements #BerkRick Moo (i): NO_INFO_MODE # # Enable the first level of enhanced information. BerkRick Moo (i): INFO_MODE1 # # INFO_MODE2-6 enable more and more until 6 is a full blown info-borg # # # Enable BRMi's code which provides minimal information about # touching planets, bombing etcetera. # #BerkRick Moo (i):0.00.0: TRAINER_MODE # # # BRM SOUND CLIENT FEATURES # BRM-Sound: WHY_DEAD BRM-Sound*confirm:0.99.0: INFO Welcome to the world of Sounds. #BRM-Sound: NO_SMARTMACROS #BRM-Sound: NO_SMARTDISTRESS # # # BRM - HADLEY FEATURES # BRM-Hadley*confirm:1.4.0: WHY_DEAD # # # Paradise CLIENT FEATURES # paradise 2*confirm:2.2 patch 11au: WHY_DEAD paradise 2*confirm:2.2 patch 11au: NO_CONTINUOUS_MOUSE #paradise 2*confirm:2.2 patch 11au: NO_NEWMACRO #paradise 2*confirm:2.2 patch 11au: NO_SMARTMACRO #paradise 2*confirm:2.2 patch 11au: RC_DISTRESS --- NEW FILE: Makefile --- # # Makefile for MetaServerII # #CC = purify -log-file="/home/netrek/metaserver/purify.log" -show-directory=yes -show-pc=yes -user-path="/home/netrek/metaserver" gcc CC = gcc CFILES = main.c scan.c server.c disp_old.c disp_new.c disp_web.c disp_udp.c disp_info.c BecomeDaemon.c OFILES = $(CFILES:.c=.o) HDRS = meta.h packets.h copyright2.h TARGET = metaserverII UTSLIBS = -lbsd -lsocket #CFLAGS = -O -s -DSYSV -DDEBUG #CFLAGS = -O -s -DDEBUG #CFLAGS = -O -s #CFLAGS = -O -s -DSYSV CFLAGS = -ggdb -DSYSV #CFLAGS = -g -DDEBUG #CFLAGS = -g -p -DPROF LIBS = # Amdahl UTS stuff #LIBS = $(UTSLIBS) #LIBS = -lnsl -lsocket # hpux stuff #LIBS = -lBSD # Solaris stuff #LIBS = -lsocket -lnsl all: $(TARGET) $(TARGET): $(OFILES) $(CC) $(CFLAGS) -o $(TARGET) $(OFILES) $(LIBS) $(OFILES): meta.h tar: tar cvf metaII.tar metarc rsa_keys Makefile features \ metaII_info server_faq $(CFILES) $(HDRS) # sample.metarc Customers $(CFILES) $(HDRS) clean: -rm -f $(OFILES) clobber: clean -rm -f $(TARGET) --- NEW FILE: rsa_keys --- ############################################################################### # RSA key list (for server administrators only; you don't need this to play) # Last update: Tue May 18 10:50 PDT 1999 # # Contact metaserver at us.netrek.org for information and # administrative requests (e.g., if you want to have a key added, e-mail # it to clientkeys at clientkeys.netrek.org ). # # When submitting keys, don't forget to include (1) if it's reserved.c # blessed, and (2) if it'll be available for FTP, specify the site. # # You need a program to convert from this format to the binary format used # be the server itself; look for keycomp.tar.Z on pittslug.sug.org. Please # use the newer key generation tools that create keys in this format. # ################################################################################ # # Bronco Clients - ick # [...1161 lines suppressed...] :pk=c3915350005759ed5f831e63a5358547de04282c187d8874b0457ea979b88b0d: # # # NetrekXP-RSA-Key-Win32:ct=NetrekXP:cr=ssheldon at sodablue.org:\ :cd=July 2001:ar=Win32:cl=inl,standard2:\ :cm=Netrek XP for Windows 9x/NT/2000/XP:\ :gk=0ff451661e2c8af6d79450af271617d38c0322e77195fea59fae0592139d3306:\ :pk=d74853b1837ab65914ab90aa563e4c0cab47698b0a86a290cb2bded894983704: # NetrekXP-Mod-RSA-Key-Win32:ct=NetrekXP Mod:cr=keyos at keyos.org:\ :cd=January 2003:ar=Win32:cl=inl,standard2:\ :cm=NetrekXP Modification for Windows 9x/NT/2000/XP:\ :gk=b160145c4205bba8815db0a908fcac9b99a0303ee44c204469374109e09bb782:\ :pk=8b0f3d3f1b1b8528d86545d3be9d8900f2e3130cb52248df9a5ec63ff9a15e11: # # # Temp development keys # # --- end of list --- --- NEW FILE: scan.c --- /* * scan.c - scan the various servers * * MetaServerII * Copyright (c) 1993 by Andy McFadden * * $Id: scan.c,v 1.1 2006/02/14 06:43:11 unbelver Exp $ * */ #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> [...1406 lines suppressed...] /* * wrapper for the web stuff in case I want to limit # of simultaneous * connections. * * Yup. it looks like I do. */ void do_web(int idx, int port_idx) { if (numweb > MAX_NUM_WEB) { log_msg("do_web: too many active web processes"); return; } else { /* count number of processes and fork */ numweb++; web_fork(idx, port_idx); } } --- NEW FILE: disp_udp.c --- /* * disp_udp.c - output to client via returned buffer */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include "meta.h" char * display_udp(int *length) { SERVER *sp; int srv, *sorted, count; char *buffer, *bp; time_t now = time(NULL); int extendedstatus; /* sort and count the servers that will be returned */ sorted = sort_servers(); count = 0; for (srv = 0; srv < server_count; srv++) { sp = &servers[sorted[srv]]; if (sp->status == SS_WORKING) sp = &prev_info; if (sp->status == SS_INIT) continue; if (sp->status == SS_WORKING) continue; count++; } /* estimate memory required and allocate it, match with sprintf()'s */ buffer = malloc((MAX_HOSTNAME+1 +6+1 +3+1 +6+1 +3+1 +3+1 +1+1 +1)*count+6); /* host port stat age play que type \n */ /* out of memory? */ if ( buffer == NULL ) return ""; bp = buffer; /* include the count at the top to ease the client's coding */ sprintf(bp,"r,%d\n",count); bp += strlen(bp); for (srv = 0; srv < server_count; srv++) { sp = &servers[sorted[srv]]; if (sp->status == SS_WORKING) sp = &prev_info; if (sp->status == SS_INIT) continue; if (sp->status == SS_WORKING) continue; /* this if structure is to satisfy COW's server filtering based on */ /* the TCP output of the metaserver. TCP was text, this is only #s */ if (sp->status == SS_NOCONN ) extendedstatus = (sp->why_dead == WD_TIMEOUT) ? 6 : 4; else extendedstatus = sp->status; sprintf(bp,"%s,%d,%d,%d,%d,%d,%c\n", sp->hostname, /* host name of server */ sp->port, /* port number of server */ extendedstatus, /* metaserver status code */ now - sp->last_update, /* age of data in seconds */ sp->player_count, /* count of players on server */ sp->queue_size, /* length of wait queue */ sp->type[0] ); /* type code, B, P, etc */ bp += strlen(bp); } *length = strlen(buffer); /* for future optimisation */ return buffer; /* caller must free buffer! */ } --- NEW FILE: serverlist --- [brain] ~/netrek/metaserver/src/metaserver%telnet pickled.fox.cs.cmu.edu 2591 Trying 128.2.181.129... Connected to pickled.fox.cs.cmu.edu. Escape character is '^]'. <>=======================================================================<> Pl: Rank Name Login Display Type <>=======================================================================<> F1: Lieutenant guest peterm #S.Berkeley.EDU SC F2: Captain Quayle_for_'99 Bozo 141.117.90.228 CA O3: Commodore Mojo_Riser tom #o.Berkeley.EDU CA O4: Lt. Cmdr. spiderman netrek #0.nycap.rr.com CA F5: Commander Funky_Buddha Bozo #0.sympatico.ca CA O6: Lt. Cmdr. guest puckett 208.197.220.173 CA F7: Flt. Capt. Jan_Brady SGO #4.dial.umd.edu CA O8: Commodore _____ psychos fusion.blm.net CA F9: Ensign redford OEM #A8.ipt.aol.com CA Fa: Ensign #d1.tx.home.com CA Oc: Ensign burger_king netrek #llatlantic.net CA Fe: Lieutenant KickButtMan! KickButtMa #78.peganet.com CA Of: Lt. Cmdr. peskylilw00bie jivey 206.147.83.29 SC Fg: Ensign guest Lab1 #mmons.ucla.edu CA Oh: Ensign dui-na__ g8vinny #df.toronto.edu CA <>=======================================================================<> Feds: 7 Roms: 0 Kli: 0 Ori: 7 No wait queue. <>=======================================================================<> --- NEW FILE: PROJECTS --- List of things to be done. - port 1080 web display is blank for most browsers unless they are a long way from the metaserver. [rec.games.netrek, "Server List", May 1999] [fixed by forking off a different process to handle web requests -c.v] --- NEW FILE: disp_old.c --- /* * disp_old.c - output in format compatible with older metaserver * (also has Uprintf() and sort_servers() routines) * * MetaServerII * Copyright (c) 1993 by Andy McFadden * * $Id: disp_old.c,v 1.1 2006/02/14 06:43:11 unbelver Exp $ * */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include "meta.h" int display_old(idx, port_idx) int idx; { USER *up; SERVER *sp; time_t now; int srv, *sorted; char buf[80], buf2[80]; up = &users[idx]; up->data_size = up->buf_size = up->pos = 0; now = time(0); /* print header */ Uprintf(idx, "\n\nMETASERVER-II %s, %s\n", VERSION, VDATE); Uprintf(idx, "Server Check period: %d minutes\n", wait_open); now = time(0); /* print player info */ sorted = sort_servers(); for (srv = 0; srv < server_count; srv++) { /* (need to sort these?) */ sp = &servers[sorted[srv]]; if (sp->status == SS_WORKING) sp = &prev_info; sprintf(buf, "%s(%d)", sp->hostname, sp->port); Uprintf(idx, "%-40.40s: ", buf); strncpy(buf, ctime(&sp->last_update)+11, 8); *(buf+8) = '\0'; strncpy(buf2, ctime(&sp->down_time)+4, 15); *(buf2+15) = '\0'; switch (sp->status) { case SS_INIT: Uprintf(idx, "Haven't yet checked this one...\n"); break; case SS_WORKING: Uprintf(idx, "(scanning server now)\n"); break; case SS_NOCONN: /* Uprintf(idx, "Couldn't connect as of %s\n", buf); */ Uprintf(idx, "Down as of %s\n", buf2); break; case SS_EMPTY: case SS_OPEN: Uprintf(idx, "%2d Players online at %s\n", sp->player_count, buf); break; case SS_QUEUE: Uprintf(idx, "Wait queue %2d at %s\n", sp->queue_size, buf); break; default: /* huh? */ fprintf(stderr, "Internal ERROR: strange state in disp_new\n"); break; } } /* print footer */ Uprintf(idx, "\n\tThat's all!\n\n"); } #define INITIAL_SIZE 64000 #define MAX_PRINT 256 #ifdef BAD_IDEA # define EXPAND_SIZE 4096 /* must be > MAX_PRINT */ #endif /* * Print something to an expandable buffer. Works like sprintf(), mostly. * Assumes that nothing we're passed will exceed MAX_PRINT bytes. * * When everybody has vsprintf, I'll do this right. */ void Uprintf(idx, format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) int idx; char *format; long arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7; { USER *up; if (idx > 1024 || idx < 0) { fprintf(stderr, "Internal ERROR: bogus idx %d\n", idx); return; } up = &users[idx]; if (!up->data_size) { if (up->buf != NULL) { /* debug */ printf("HEY: Reassigning non-null buf for %d\n", idx); /* endif */ log_msg("Reassigning non-null buf for %d\n", idx); /* ought to free() here, but that might make us crash */ } if ((up->buf = (char *) malloc(INITIAL_SIZE)) == NULL) { fprintf(stderr, "malloc failure!\n"); log_msg("exit: malloc failure in Uprintf"); exit(1); } up->buf_size = INITIAL_SIZE; } if (up->buf_size - up->data_size < MAX_PRINT) { #ifdef BAD_IDEA /* buffer too small, realloc */ up->buf_size += EXPAND_SIZE; if ((up->buf = (char *) realloc(up->buf, up->buf_size)) == NULL) { fprintf(stderr, "realloc failure!\n"); log_msg("exit: realloc failure in Uprintf"); exit(1); } #else /* ran out of room; bummer */ log_msg("NOTE: ran out of space in Uprintf()"); #endif } sprintf(up->buf + up->data_size, format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); up->data_size += strlen(up->buf + up->data_size); } static int *alpha = NULL; /* alphabetical host sorting order */ /* * Comparison function for qsort() */ static int srvcmp(elmp1, elmp2) int *elmp1, *elmp2; { register SERVER *s1, *s2; register int elm1 = *elmp1, elm2 = *elmp2; int st, pc, ho, po; s1 = &servers[elm1]; if (s1->status == SS_WORKING) s1 = &prev_info; s2 = &servers[elm2]; if (s2->status == SS_WORKING) s2 = &prev_info; st = s1->status - s2->status; if (s1->status == SS_QUEUE) pc = s1->queue_size - s2->queue_size; else if (s1->status == SS_NOCONN) pc = 0; else pc = s1->player_count - s2->player_count; ho = alpha[elm1] - alpha[elm2]; po = s1->port - s2->port; /*if ((st > 0) || (!st && pc < 0) || (!st && !pc && ho > 0))*/ if ((st < 0) || (!st && pc > 0) || (!st && !pc && ho > 0) || (!st && !pc && !ho && po < 0)) return (1); else return (-1); /* note there should not be a case in which two entries match exactly */ } /* * Returns a pointer to an array of ints, indicating the sorted order of * the servers. * * Sorting order is: * status * player_count/queue_size * alphabetical hostname * port number * * IDEA: we can reduce the CPU usage by sorting the internal list by * host and port number once, during initialization. However, this would * require a stable sort on the other categories, which qs doesn't * guarantee. */ int * sort_servers() { static int *sorted = NULL; register int i, j, *ip; int k; /* allocate some stuff first time through */ if (sorted == NULL) { if (verbose) printf("Allocated sort_servers array\n"); /* DEBUG */ if ((sorted = (int *) malloc(server_count * sizeof(int))) == NULL) { fprintf(stderr, "malloc failure in sort_servers\n"); log_msg("exit: malloc failure in sort_servers"); exit(1); } /* to ease the CPU load, compute the alphabetical order once */ if ((alpha = (int *) malloc(server_count * sizeof(int))) == NULL) { fprintf(stderr, "malloc failure in sort_servers (alpha)\n"); log_msg("exit: malloc failure in sort_servers (alpha)"); exit(1); } for (i = 0, ip = sorted; i < server_count; i++, ip++) *ip = i; for (i = 0; i < server_count-1; i++) for (j = i+1; j < server_count; j++) if (strcmp(servers[sorted[i]].hostname, servers[sorted[j]].hostname) > 0) { k = sorted[i]; sorted[i] = sorted[j]; sorted[j] = k; } /* change sorted indices into parallel table of relative positions */ for (i = 0, ip = sorted; i < server_count; i++, ip++) alpha[*ip] = i; } /* intialize the sorted array to itself */ for (i = 0, ip = sorted; i < server_count; i++, ip++) *ip = i; /* sort the array with the system qsort */ qsort((char *) sorted, server_count, sizeof(int), srvcmp); return (sorted); } --- NEW FILE: disp_new.c --- /* * disp_new.c - output in fancy new format * * MetaServerII * Copyright (c) 1993 by Andy McFadden * * $Id: disp_new.c,v 1.1 2006/02/14 06:43:11 unbelver Exp $ * */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/fcntl.h> #include "meta.h" #define SEMI_VERBOSE /* not quite fully verbose */ /* "when" should be the length of time in seconds */ char * nice_time(when) time_t when; { static char buf[64]; if (when < 120) sprintf(buf, "%d seconds", when); else if (when/60 < 120) sprintf(buf, "%d minutes", when/60); else if (when/3600 < 48) sprintf(buf, "%d hours", when/3600); else sprintf(buf, "%d days", when/(3600*24)); return (buf); } int display_new(idx, port_idx) int idx, port_idx; { register int i; USER *up; SERVER *sp; time_t now; int srv, ago, *sorted; char flagbuf[8], buf[128]; char *cp; /* int n, hackfd; char hackbuf[80]; */ up = &users[idx]; up->data_size = up->buf_size = up->pos = 0; now = time(0); /* print header */ Uprintf(idx, "\n*** Connected to MetaServerII v%s on port %d ***\n", VERSION, ports[port_idx].port); cp = ctime(&now); cp[strlen(cp)-1] = '\0'; /* playing with fire? */ Uprintf(idx, "Date in %s is %s (data spans %s)\n", location, cp, nice_time(now-checktime)); Uprintf(idx, "E-mail administrative requests to %s.\n\n", admin); Uprintf(idx, "Retry periods (in minutes): "); Uprintf(idx, "down:%d empty:%d open:%d queue:%d\n\n", wait_noconn, wait_empty, wait_open, wait_queue); Uprintf(idx, "Other interesting MetaServerII ports: "); for (i = 0; i < port_count; i++) { if (ports[i].kind != DI_NEW) Uprintf(idx, "%d ", ports[i].port); } Uprintf(idx, "\n\n"); Uprintf(idx, "%-39s %-8s %-3s %s\n", "", "", "Mins", ""); Uprintf(idx, "%-39s %-8s %-3s %-17s %s\n", "Server Host","Port","Ago", "Status", "Flags"); Uprintf(idx, "--------------------------------------- "); Uprintf(idx, "-------- ---- ----------------- -------\n"); /* print player info */ sorted = sort_servers(); for (srv = 0; srv < server_count; srv++) { /* (need to sort these?) */ sp = &servers[sorted[srv]]; if (sp->status == SS_WORKING) sp = &prev_info; Uprintf(idx, "-h %-36.36s -p %-5d ", sp->hostname, sp->port); strcpy(flagbuf, " "); for (i = 0; i < 6; i++) if (sp->display_flags & flags[i].bit) flagbuf[i] = flags[i].chr; flagbuf[6] = sp->type[0]; /* hack in the type field */ ago = (now - sp->last_update) / 60; switch (sp->status) { case SS_INIT: Uprintf(idx, "(no data yet)\n"); break; case SS_WORKING: Uprintf(idx, "(scanning server now)\n"); break; case SS_NOCONN: switch (sp->why_dead) { case WD_CONN: Uprintf(idx, "%3d %-17s %s\n", ago, "* No connection", flagbuf); break; case WD_READ: Uprintf(idx, "%3d %-17s %s\n", ago, "* Bad read", flagbuf); break; case WD_GARBAGE: Uprintf(idx, "%3d %-17s %s\n", ago, "* Garbaged read", flagbuf); break; case WD_TIMEOUT: Uprintf(idx, "%3d %-17s %s\n", ago, "* Timed out", flagbuf); break; case WD_FLOODING: Uprintf(idx, "%3d %-17s %s\n", ago, "* Flooding", flagbuf); break; default: Uprintf(idx, "%3d %-17s %s\n", ago, "* Not responding", flagbuf); break; } break; case SS_EMPTY: Uprintf(idx, "%3d %-17s %s\n", ago, "Nobody playing", flagbuf); break; case SS_OPEN: sprintf(buf, "OPEN: %d player%s", sp->player_count, (sp->player_count != 1) ? "s" : ""); Uprintf(idx, "%3d %-17s %s\n", ago, buf, flagbuf); break; case SS_QUEUE: sprintf(buf, "Wait queue: %d", sp->queue_size); Uprintf(idx, "%3d %-17s %s\n", ago, buf, flagbuf); break; default: /* huh? */ fprintf(stderr, "Internal ERROR: strange state in disp_new\n"); break; } } /* quick hack to print US metaserver info */ /* if ((hackfd = open ( "usfile", O_RDONLY )) < 0 ) Uprintf(idx, "\nNo US-metaserver info now, sorry.\n"); else { lseek( hackfd, 0L, SEEK_SET ); while ((n = read (hackfd, hackbuf, 80)) > 0) { write(idx, hackbuf, n); } close(hackfd); } */ /* print footer */ Uprintf(idx, "\nThat's it!\n"); return(0); } /* * Do the "verbose" output format */ int display_verbose(idx, port_idx) int idx; { USER *up; SERVER *sp; time_t now; int srv, ago, *sorted; char buf[80]; int i; up = &users[idx]; up->data_size = up->buf_size = up->pos = 0; now = time(0); /* print header */ Uprintf(idx, "\n*** Connected to MetaServerII v%s on port %d ***\n", VERSION, ports[port_idx].port); Uprintf(idx, "Date in %s is %s", location, ctime(&now)); Uprintf(idx, "E-mail administrative requests to %s.\n", admin); Uprintf(idx, "Up continuously for %s", nice_time(now-uptime)); if (uptime != checktime) Uprintf(idx, " (data spans %s).\n", nice_time(now-checktime)); else Uprintf(idx, ".\n"); Uprintf(idx, "Retry periods (in minutes): "); Uprintf(idx, "down:%d empty:%d open:%d queue:%d\n\n", wait_noconn, wait_empty, wait_open, wait_queue); Uprintf(idx, "Other interesting MetaServerII ports:\n"); for (i = 0; i < port_count; i++) if (ports[i].kind != DI_VERBOSE) Uprintf(idx, "\t%d - %s\n", ports[i].port, ports[i].desc); Uprintf(idx, "\n"); /* print player info */ sorted = sort_servers(); for (srv = 0; srv < server_count; srv++) { /* (need to sort these?) */ sp = &servers[sorted[srv]]; if (sp->status == SS_WORKING) sp = &prev_info; #ifdef SEMI_VERBOSE if (sp->status == SS_NOCONN || sp->status == SS_EMPTY) continue; #endif Uprintf(idx, "Server: %s [%c], port %d", sp->hostname, sp->type[0], sp->port); /* print comment if we've got one */ if (strlen(sp->comment) > 0) Uprintf(idx, " (%s)\n", sp->comment); else Uprintf(idx, "\n"); ago = (now - sp->last_update) / 60; strncpy(buf, ctime(&sp->last_update)+11, 8); *(buf+8) = '\0'; if (sp->status != SS_INIT && sp->status != SS_WORKING) Uprintf(idx, "Last checked at %s (%d minute%s ago).\n", buf, ago, (ago == 1) ? "" : "s"); Uprintf(idx, "\n"); switch (sp->status) { case SS_INIT: Uprintf(idx, "This server has not been examined yet.\n"); break; case SS_WORKING: Uprintf(idx, "Information for this server is being retrieved right now.\n"); break; case SS_NOCONN: Uprintf(idx, "Index %d\n", sp->why_dead); switch (sp->why_dead) { case WD_UNKNOWN: Uprintf(idx, "The server is not responding.\n"); break; case WD_CONN: Uprintf(idx, "Unable to connect to server.\n"); break; case WD_READ: Uprintf(idx, "Got errors reading from server.\n"); break; case WD_GARBAGE: Uprintf(idx, "Server sent garbled data.\n"); break; case WD_TIMEOUT: Uprintf(idx, "Timed out waiting for response.\n"); break; case WD_FLOODING: Uprintf(idx, "Flooding metaserver, temporarily delisted.\n"); break; default: log_msg("got weird sp->why_dead (%d) in display_verbose()", sp->why_dead); Uprintf(idx, "The server is not responding.\n"); } break; case SS_EMPTY: Uprintf(idx, "Nobody is playing.\n"); break; case SS_OPEN: if (sp->player_count == 1) Uprintf(idx, "There is 1 person playing here.\n"); else Uprintf(idx, "There are %d people playing here.\n", sp->player_count); print_players(idx, sorted[srv]); break; case SS_QUEUE: if (sp->queue_size == 1) Uprintf(idx, "There is a wait queue with 1 person on it.\n"); else Uprintf(idx, "There is a wait queue with %d people on it.\n", sp->queue_size); print_players(idx, sorted[srv]); break; default: /* huh? */ fprintf(stderr, "Internal ERROR: strange state in disp_verbose\n"); break; } Uprintf(idx, "\n\n"); } /* print footer */ Uprintf(idx, "\nThat's it!\n\n"); return (0); } #define NOBODY 0x0 #define IND 0x0 #define FED 0x1 #define ROM 0x2 #define KLI 0x4 #define ORI 0x8 #define ALLTEAM (FED|ROM|KLI|ORI) #define MAXTEAM (ORI) #define NUMTEAM 4 #define NUMRANKS 9 #define PNUMRANKS 18 struct rank { char *name; }; static struct rank ranks[NUMRANKS+1] = { { "Ensign"}, { "Lieutenant"}, { "Lt. Cmdr."}, { "Commander"}, { "Captain"}, { "Flt. Capt."}, { "Commodore"}, { "Rear Adm."}, { "Admiral"}, { "(unknown)"} }; static struct rank pranks[PNUMRANKS] = { { "Recruit"}, { "Specialist"}, { "Cadet"}, { "Midshipman"}, { "Ensn., J.G."}, { "Ensign"}, { "Lt., J.G."}, { "Lieutenant"}, { "Lt. Cmdr."}, { "Commander"}, { "Captain"}, { "Fleet Capt."}, { "Commodore"}, { "Moff"}, { "Grand Moff"}, { "Rear Adml."}, { "Admiral"}, { "Grand Adml."} }; /*static*/ int print_players(idx, srv) int idx, srv; { SERVER *sp; PLAYER *j; int i, k, t, ci; int plcount, fromm; char namebuf[18]; Uprintf(idx, "\n"); sp = &servers[srv]; /* first, scan list */ plcount = 0; for (i = 0; i < sp->max_players; i++) if (sp->players[i].p_status!=PFREE && sp->players[i].p_status!=POUTFIT) plcount++; if (!plcount) { Uprintf(idx, "(Player list not available.)\n"); return (0); } fromm = (sp->status == SS_QUEUE); if (fromm) Uprintf(idx, "(player list from port %d; may not be 100%% accurate)\n", sp->port-1); /* this was adapted from ck_players */ Uprintf(idx, " Type Rank Name Login\n"); /* for (k = IND; k <= ORI; k<<=1) {*/ for (t = 0; t <= NUMTEAM; t++) { if (!t) k = 0; else k = 1 << (t-1); for (i = 0, j = &sp->players[i]; i < sp->max_players; i++, j++) { if ((j->p_status != PFREE && j->p_status != POUTFIT) && j->p_team == k) { /* ugly way to ferret out trailing spaces */ strncpy(namebuf+1, j->p_name, 16); namebuf[0] = ' '; namebuf[16] = namebuf[17] = '\0'; if (namebuf[strlen(namebuf)-1] == ' ') { namebuf[0] = '"'; namebuf[strlen(namebuf)] = '"'; } for (ci = 0; ci < 18; ci++) /* weed out control chars */ if (namebuf[ci] && ((namebuf[ci] & 0x7f) < 0x20)) namebuf[ci] = '^'; Uprintf(idx, "%c%c %s %-9.9s %-18.18s %s@%s\n", teamlet[j->p_team], shipnos[i], /*classes[j->ship_type],*/ (sp->type[0] != 'P') ? ((j->ship_type<NUM_TYPES) ? classes[j->ship_type]:"!!") : ((j->ship_type<PNUM_TYPES) ? pclasses[j->ship_type]:"!!"), (sp->type[0] != 'P') ? ((j->p_rank<NUMRANKS) ? ranks[j->p_rank].name:"(unknown)") : ((j->p_rank<PNUMRANKS) ? pranks[j->p_rank].name:"(unknown)"), namebuf, /*j->p_name,*/ j->p_login, j->p_monitor ); } } } } --- NEW FILE: packets.h --- /* * Include file for socket I/O xtrek. * * Kevin P. Smith 1/29/89 * * $Id: packets.h,v 1.1 2006/02/14 06:43:11 unbelver Exp $ */ /* the following typedefs allow portability to machines without the ubiquitous 32-bit architecture (KSR1, Cray, DEC Alpha) */ typedef unsigned int CARD32; typedef unsigned short CARD16; typedef unsigned char CARD8; typedef int INT32; typedef short INT16; #if __STDC__ || defined(sgi) || defined(AIXV3) typedef signed char INT8; #else /* stupid compilers */ typedef char INT8; #endif /* packets sent from xtrek server to remote client */ #define SP_MESSAGE 1 #define SP_PLAYER_INFO 2 /* general player info not elsewhere */ #define SP_KILLS 3 /* # kills a player has */ #define SP_PLAYER 4 /* x,y for player */ #define SP_TORP_INFO 5 /* torp status */ #define SP_TORP 6 /* torp location */ #define SP_PHASER 7 /* phaser status and direction */ #define SP_PLASMA_INFO 8 /* player login information */ #define SP_PLASMA 9 /* like SP_TORP */ #define SP_WARNING 10 /* like SP_MESG */ #define SP_MOTD 11 /* line from .motd screen */ #define SP_YOU 12 /* info on you? */ #define SP_QUEUE 13 /* estimated loc in queue? */ #define SP_STATUS 14 /* galaxy status numbers */ #define SP_PLANET 15 /* planet armies & facilities */ #define SP_PICKOK 16 /* your team & ship was accepted */ #define SP_LOGIN 17 /* login response */ #define SP_FLAGS 18 /* give flags for a player */ #define SP_MASK 19 /* tournament mode mask */ #define SP_PSTATUS 20 /* give status for a player */ #define SP_BADVERSION 21 /* invalid version number */ #define SP_HOSTILE 22 /* hostility settings for a player */ #define SP_STATS 23 /* a player's statistics */ #define SP_PL_LOGIN 24 /* new player logs in */ #define SP_RESERVED 25 /* for future use */ #define SP_PLANET_LOC 26 /* planet name, x, y */ /* paradise packets */ #define SP_SCAN 27 /* scan packet */ #define SP_UDP_REPLY 28 /* notify client of UDP status */ #define SP_SEQUENCE 29 /* sequence # packet */ #define SP_SC_SEQUENCE 30 /* this trans is semi-critical info */ #define SP_RSA_KEY 31 /* RSA data packet */ #define SP_MOTD_PIC 32 /* motd bitmap pictures */ #define SP_STATS2 33 /* new stats packet*/ #define SP_STATUS2 34 /* new status packet*/ #define SP_PLANET2 35 /* new planet packet*/ #define SP_NEW_MOTD 36 /* New MOTD info notification uses */ #define SP_THINGY 37 /* thingy location */ #define SP_THINGY_INFO 38 /* thingy status */ #define SP_SHIP_CAP 39 /* ship capabilities */ /* packets sent from remote client to xtrek server */ #define CP_MESSAGE 1 /* send a message */ #define CP_SPEED 2 /* set speed */ #define CP_DIRECTION 3 /* change direction */ #define CP_PHASER 4 /* phaser in a direction */ #define CP_PLASMA 5 /* plasma (in a direction) */ #define CP_TORP 6 /* fire torp in a direction */ #define CP_QUIT 7 /* self destruct */ #define CP_LOGIN 8 /* log in (name, password) */ #define CP_OUTFIT 9 /* outfit to new ship */ #define CP_WAR 10 /* change war status */ #define CP_PRACTR 11 /* create practice robot? */ #define CP_SHIELD 12 /* raise/lower sheilds */ #define CP_REPAIR 13 /* enter repair mode */ #define CP_ORBIT 14 /* orbit planet/starbase */ #define CP_PLANLOCK 15 /* lock on planet */ #define CP_PLAYLOCK 16 /* lock on player */ #define CP_BOMB 17 /* bomb a planet */ #define CP_BEAM 18 /* beam armies up/down */ #define CP_CLOAK 19 /* cloak on/off */ #define CP_DET_TORPS 20 /* detonate enemy torps */ #define CP_DET_MYTORP 21 /* detonate one of my torps */ #define CP_COPILOT 22 /* toggle copilot mode */ #define CP_REFIT 23 /* refit to different ship type */ #define CP_TRACTOR 24 /* tractor on/off */ #define CP_REPRESS 25 /* pressor on/off */ #define CP_COUP 26 /* coup home planet */ #define CP_SOCKET 27 /* new socket for reconnection */ #define CP_OPTIONS 28 /* send my options to be saved */ #define CP_BYE 29 /* I'm done! */ #define CP_DOCKPERM 30 /* set docking permissions */ #define CP_UPDATES 31 /* set number of usecs per update */ #define CP_RESETSTATS 32 /* reset my stats packet */ #define CP_RESERVED 33 /* for future use */ #define SOCKVERSION 4 struct packet_handler { int size; int (*handler)(); }; struct mesg_spacket { char type; /* SP_MESSAGE */ unsigned char m_flags; unsigned char m_recpt; unsigned char m_from; char mesg[80]; }; struct plyr_info_spacket { char type; /* SP_PLAYER_INFO */ char pnum; char shiptype; char team; }; struct plyr_login_spacket { char type; /* SP_PL_LOGIN */ char pnum; char rank; char pad1; char name[16]; char monitor[16]; char login[16]; }; struct hostile_spacket { char type; /* SP_HOSTILE */ char pnum; char war; char hostile; }; struct stats_spacket { char type; /* SP_STATS */ char pnum; char pad1; char pad2; long tkills; /* Tournament kills */ long tlosses; /* Tournament losses */ long kills; /* overall */ long losses; /* overall */ long tticks; /* ticks of tournament play time */ long tplanets; /* Tournament planets */ long tarmies; /* Tournament armies */ long sbkills; /* Starbase kills */ long sblosses; /* Starbase losses */ long armies; /* non-tourn armies */ long planets; /* non-tourn planets */ long maxkills; /* max kills as player * 100 */ long sbmaxkills; /* max kills as sb * 100 */ }; struct flags_spacket { char type; /* SP_FLAGS */ char pnum; /* whose flags are they? */ char pad1; char pad2; unsigned flags; }; struct kills_spacket { char type; /* SP_KILLS */ char pnum; char pad1; char pad2; unsigned kills; /* where 1234=12.34 kills and 0=0.00 kills */ }; struct player_spacket { char type; /* SP_PLAYER */ char pnum; unsigned char dir; char speed; long x,y; }; struct torp_info_spacket { char type; /* SP_TORP_INFO */ char war; char status; /* TFREE, TDET, etc... */ char pad1; /* pad needed for cross cpu compatibility */ short tnum; short pad2; }; struct torp_spacket { char type; /* SP_TORP */ unsigned char dir; short tnum; long x,y; }; struct phaser_spacket { char type; /* SP_PHASER */ char pnum; char status; /* PH_HIT, etc... */ unsigned char dir; long x,y; long target; }; struct you_spacket { char type; /* SP_YOU */ char pnum; /* Guy needs to know this... */ char hostile; char swar; char armies; char pad1; char pad2; char pad3; unsigned flags; long damage; long shield; long fuel; short etemp; short wtemp; short whydead; short whodead; }; struct status_spacket { char type; /* SP_STATUS */ char tourn; char pad1; char pad2; unsigned armsbomb; unsigned planets; unsigned kills; unsigned losses; unsigned time; unsigned long timeprod; }; struct warning_spacket { char type; /* SP_WARNING */ char pad1; char pad2; char pad3; char mesg[80]; }; struct planet_spacket { char type; /* SP_PLANET */ char pnum; char owner; char info; short flags; short pad2; long armies; }; struct torp_cpacket { char type; /* CP_TORP */ unsigned char dir; /* direction to fire torp */ char pad1; char pad2; }; struct phaser_cpacket { char type; /* CP_PHASER */ unsigned char dir; char pad1; char pad2; }; struct speed_cpacket { char type; /* CP_SPEED */ char speed; char pad1; char pad2; }; struct dir_cpacket { char type; /* CP_DIRECTION */ unsigned char dir; char pad1; char pad2; }; struct shield_cpacket { char type; /* CP_SHIELD */ char state; /* up/down */ char pad1; char pad2; }; struct repair_cpacket { char type; /* CP_REPAIR */ char state; /* on/off */ char pad1; char pad2; }; struct orbit_cpacket { char type; /* CP_ORBIT */ char state; /* on/off */ char pad1; char pad2; }; struct practr_cpacket { char type; /* CP_PRACTR */ char pad1; char pad2; char pad3; }; struct bomb_cpacket { char type; /* CP_BOMB */ char state; char pad1; char pad2; }; struct beam_cpacket { char type; /* CP_BEAM */ char state; char pad1; char pad2; }; struct cloak_cpacket { char type; /* CP_CLOAK */ char state; char pad1; char pad2; }; struct det_torps_cpacket { char type; /* CP_DET_TORPS */ char pad1; char pad2; char pad3; }; struct copilot_cpacket { char type; /* CP_COPLIOT */ char state; char pad1; char pad2; }; struct queue_spacket { char type; /* SP_QUEUE */ char pad1; short pos; }; struct outfit_cpacket { char type; /* CP_OUTFIT */ char team; char ship; char pad1; }; struct pickok_spacket { char type; /* SP_PICKOK */ char state; char pad2; char pad3; }; struct login_cpacket { char type; /* CP_LOGIN */ char query; char pad2; char pad3; char name[16]; char password[16]; char login[16]; }; struct login_spacket { char type; /* SP_LOGIN */ char accept; /* 1/0 */ char pad2; char pad3; long flags; char keymap[96]; }; struct tractor_cpacket { char type; /* CP_TRACTOR */ char state; char pnum; char pad2; }; struct repress_cpacket { char type; /* CP_REPRESS */ char state; char pnum; char pad2; }; struct det_mytorp_cpacket { char type; /* CP_DET_MYTORP */ char pad1; short tnum; }; struct war_cpacket { char type; /* CP_WAR */ char newmask; char pad1; char pad2; }; struct refit_cpacket { char type; /* CP_REFIT */ char ship; char pad1; char pad2; }; struct plasma_cpacket { char type; /* CP_PLASMA */ unsigned char dir; char pad1; char pad2; }; struct plasma_info_spacket { char type; /* SP_PLASMA_INFO */ char war; char status; /* TFREE, TDET, etc... */ char pad1; /* pad needed for cross cpu compatibility */ short pnum; short pad2; }; struct plasma_spacket { char type; /* SP_PLASMA */ char pad1; short pnum; long x,y; }; struct playlock_cpacket { char type; /* CP_PLAYLOCK */ char pnum; char pad1; char pad2; }; struct planlock_cpacket { char type; /* CP_PLANLOCK */ char pnum; char pad1; char pad2; }; struct coup_cpacket { char type; /* CP_COUP */ char pad1; char pad2; char pad3; }; struct pstatus_spacket { char type; /* SP_PSTATUS */ char pnum; char status; char pad1; }; struct motd_spacket { char type; /* SP_MOTD */ char pad1; char pad2; char pad3; char line[80]; }; struct quit_cpacket { char type; /* CP_QUIT */ char pad1; char pad2; char pad3; }; struct mesg_cpacket { char type; /* CP_MESSAGE */ char group; char indiv; char pad1; char mesg[80]; }; struct mask_spacket { char type; /* SP_MASK */ char mask; char pad1; char pad2; }; struct socket_cpacket { char type; /* CP_SOCKET */ char version; char pad2; char pad3; unsigned socket; }; struct options_cpacket { char type; /* CP_OPTIONS */ char pad1; char pad2; char pad3; unsigned flags; char keymap[96]; }; struct bye_cpacket { char type; /* CP_BYE */ char pad1; char pad2; char pad3; }; struct badversion_spacket { char type; /* SP_BADVERSION */ char why; char pad2; char pad3; }; struct dockperm_cpacket { char type; /* CP_DOCKPERM */ char state; char pad2; char pad3; }; struct updates_cpacket { char type; /* CP_UPDATES */ char pad1; char pad2; char pad3; unsigned usecs; }; struct resetstats_cpacket { char type; /* CP_RESETSTATS */ char verify; /* 'Y' - just to make sure he meant it */ char pad2; char pad3; }; struct reserved_spacket { char type; /* SP_RESERVED */ char pad1; char pad2; char pad3; char data[16]; }; struct reserved_cpacket { char type; /* CP_RESERVED */ char pad1; char pad2; char pad3; char data[16]; char resp[16]; }; struct planet_loc_spacket { char type; /* SP_PLANET_LOC */ char pnum; char pad2; char pad3; long x; long y; char name[16]; }; /* paradise packets */ struct scan_spacket { /* ATM */ INT8 type; /* SP_SCAN */ INT8 pnum; INT8 success; INT8 pad1; INT32 p_fuel; INT32 p_armies; INT32 p_shield; INT32 p_damage; INT32 p_etemp; INT32 p_wtemp; }; struct udp_reply_spacket { /* UDP */ INT8 type; /* SP_UDP_REPLY */ INT8 reply; INT8 pad1; INT8 pad2; INT32 port; }; struct sequence_spacket { /* UDP */ INT8 type; /* SP_SEQUENCE */ INT8 pad1; CARD16 sequence; }; struct sc_sequence_spacket { /* UDP */ INT8 type; /* SP_CP_SEQUENCE */ INT8 pad1; CARD16 sequence; }; /* * Game configuration. * KAO 1/23/93 */ struct ship_cap_spacket { /* Server configuration of client */ INT8 type; /* screw motd method */ INT8 operation; /* 0 = add/change a ship, 1 = remove a ship */ INT16 s_type; /* SP_SHIP_CAP */ INT16 s_torpspeed; INT16 s_phaserrange; INT32 s_maxspeed; INT32 s_maxfuel; INT32 s_maxshield; INT32 s_maxdamage; INT32 s_maxwpntemp; INT32 s_maxegntemp; INT16 s_width; INT16 s_height; INT16 s_maxarmies; INT8 s_letter; INT8 pad2; INT8 s_name[16]; INT8 s_desig1; INT8 s_desig2; INT16 s_bitmap; }; struct motd_pic_spacket { INT8 type; /* SP_MOTD_PIC */ INT8 pad1; INT16 x, y, page; INT16 width, height; INT8 bits[1016]; }; /* This is used to send paradise style stats */ struct stats_spacket2 { INT8 type; /* SP_STATS2 */ INT8 pnum; INT8 pad1; INT8 pad2; INT32 genocides; /* number of genocides participated in */ INT32 maxkills; /* max kills ever * 100 */ INT32 di; /* destruction inflicted for all time * 100 */ INT32 kills; /* Kills in tournament play */ INT32 losses; /* Losses in tournament play */ INT32 armsbomb; /* Tournament armies bombed */ INT32 resbomb; /* resources bombed off */ INT32 dooshes; /* armies killed while being carried */ INT32 planets; /* Tournament planets conquered */ INT32 tticks; /* Tournament ticks */ /* SB/WB/JS stats are entirely separate */ INT32 sbkills; /* Kills as starbase */ INT32 sblosses; /* Losses as starbase */ INT32 sbticks; /* Time as starbase */ INT32 sbmaxkills; /* Max kills as starbase * 100 */ INT32 wbkills; /* Kills as warbase */ INT32 wblosses; /* Losses as warbase */ INT32 wbticks; /* Time as warbase */ INT32 wbmaxkills; /* Max kills as warbase * 100 */ INT32 jsplanets; /* planets assisted with in JS */ INT32 jsticks; /* ticks played as a JS */ INT32 rank; /* Ranking of the player */ INT32 royal; /* royaly, specialty, rank */ }; /*status info for paradise stats */ struct status_spacket2 { INT8 type; /* SP_STATUS2 */ INT8 tourn; INT8 pad1; INT8 pad2; CARD32 dooshes; /* total number of armies dooshed */ CARD32 armsbomb; /* all t-mode armies bombed */ CARD32 resbomb; /* resources bombed */ CARD32 planets; /* all t-mode planets taken */ CARD32 kills; /* all t-mode kills made */ CARD32 losses; /* all t-mode losses */ CARD32 sbkills; /* total kills in SB's */ CARD32 sblosses; /* total losses in Sb's */ CARD32 sbtime; /* total time in SB's */ CARD32 wbkills; /* kills in warbases */ CARD32 wblosses; /* losses in warbases */ CARD32 wbtime; /* total time played in wb's */ CARD32 jsplanets; /* total planets taken by jump ships */ CARD32 jstime; /* total time in a jump ship */ CARD32 time; /* t mode time in this game */ CARD32 timeprod; /* t-mode ship ticks--sort of like */ }; /*planet info for a paradise planet*/ struct planet_spacket2 { INT8 type; /* SP_PLANET2 */ INT8 pnum; /* planet number */ INT8 owner; /* owner of the planet */ INT8 info; /* who has touched planet */ INT32 flags; /* planet's flags */ INT32 timestamp; /* timestamp for info on planet */ INT32 armies; /* armies on the planet */ }; struct obvious_packet { INT8 type; /* SP_NEW_MOTD */ INT8 pad1; /* CP_ASK_MOTD */ }; struct thingy_info_spacket { INT8 type; /* SP_TORP_INFO */ INT8 war; INT16 shape; /* a thingy_types */ INT16 tnum; INT16 owner; }; struct thingy_spacket { INT8 type; /* SP_TORP */ CARD8 dir; INT16 tnum; INT32 x, y; }; struct rsa_key_spacket { INT8 type; /* SP_RSA_KEY */ INT8 pad1; INT8 pad2; INT8 pad3; CARD8 data[32]; }; struct ping_spacket { INT8 type; /* SP_PING */ CARD8 number; /* id (ok to wrap) */ CARD16 lag; /* delay of last ping in ms */ CARD8 tloss_sc; /* total loss server-client 0-100% */ CARD8 tloss_cs; /* total loss client-server 0-100% */ CARD8 iloss_sc; /* inc. loss server-client 0-100% */ CARD8 iloss_cs; /* inc. loss client-server 0-100% */ }; --- NEW FILE: meta.h --- /* * meta.h - global vars and configuration stuff * * MetaServerII * Copyright (c) 1993 by Andy McFadden * * $Id: meta.h,v 1.1 2006/02/14 06:43:11 unbelver Exp $ * */ /*#define DEBUG*/ /* files - adjust to taste */ #define LOGFILE "metalog" #define CONFIGFILE "metarc" #define CHECKFILE "metackp" /* some time constants, in seconds */ /* MAX_BUSY 20 BUSY_EXTEN 20 */ #define MAX_BUSY 60 /* max time to wait for a server */ #define BUSY_EXTEN 40 /* extension time for distant servers */ #define TIME_RES 40 /* how often do we wake up */ /*#define START_DIST 40 space initial checks by this much */ #define START_DIST 5 #define MAX_USER 10 /* after this long, bye bye user */ #define MAX_CKP_AGE 60*10 /* reject ckp files after 10 mins */ /* flood controls */ #define MAX_CALLS 3 /* you get this many calls... */ #define FLOOD_TIME 10 /* ...over this many seconds. */ /* server flood controls */ #define MIN_UREAD_TIME 58 #define MAX_WEB_TIME 30 /* maximum amount of time a web */ /* request may last in seconds */ #define MAX_WEB_BYTES 1024 /* biggest packet I'm expecting */ #define MAX_NUM_WEB 20 /* eh. 20 processes seems ok */ /* * end of configurable stuff */ /* what version we are, and date for old format */ #define VERSION "1.0.3" #define VDATE "6/1/93" /* display flags */ typedef struct { int bit; char chr; char *word; } FLAG; #define MAX_FLAG 8 /* increase to 32 as needed */ extern FLAG flags[MAX_FLAG]; #define DF_HAD_TMODE 0x0001 #define DF_ALWAYS_DEAD 0x0002 #define DF_RSA 0x0004 #define DF_TYPE 0x0040 /* not really a flag */ #define TRUE 1 #define FALSE 0 /* deal with funky SVR3 signal mechanism */ #ifdef _UTS # define SYSV #endif #if defined (SYSV) && !defined(hpux) # define SIGNAL(sig,action) sigset(sig, action) #else # define SIGNAL(sig,action) signal(sig, action) #endif /* assorted library routines */ extern char *malloc(), *realloc(); /* player info */ typedef struct { int p_status; int p_rank; int p_team; char p_mapchars[2]; char p_name[16]; char p_login[16]; char p_monitor[16]; int ship_type; } PLAYER; #define MAX_PLAYER 36 /* must be >= server MAXPLAYER */ #define NUM_TYPES 8 /* #of different ship types */ #define PNUM_TYPES 16 /* #of different ship types */ #define MAX_HOSTNAME 64 /* global structure declarations */ typedef enum { SS_WORKING, SS_QUEUE, SS_OPEN, SS_EMPTY, SS_NOCONN, SS_INIT } SERVER_STATUS; /* note: this affects sorting order */ typedef enum { WD_UNKNOWN, WD_CONN, WD_READ, WD_GARBAGE, WD_TIMEOUT, WD_FLOODING } WHY_DEAD; typedef enum { PS_PORTM, PS_PORT } PROBE_STATE; typedef enum { CS_CLOSED, CS_CONNECTING, CS_CONNECTED } CONN_STATE; typedef struct { SERVER_STATUS status; /* status of the server */ PROBE_STATE pstate; /* what we are trying to do (port or port-1) */ CONN_STATE cstate; /* what our connection status is */ WHY_DEAD why_dead; /* if status==SS_NOCONN, why? */ char hostname[MAX_HOSTNAME]; /* DNS */ char ip_addr[32]; /* ASCII IP */ long addr; /* actual address */ int port; int count; time_t down_time; time_t last_update; time_t next_update; long flags; /* internal use */ long display_flags; /* for "flags" column */ char comment[40]; char mask; /* tournament mode mask */ char type[1]; /* may expand in the future */ int max_players; int player_count; int queue_size; int solicit; /* we got this as a solicit packet */ PLAYER players[MAX_PLAYER]; } SERVER; /* (if you add pointers here, update checkpointing) */ typedef struct { char *buf; int pos; int buf_size; int data_size; time_t start_time; /* if nonzero, then user is active */ } USER; /* port stuff */ typedef enum { DI_OLD, DI_NEW, DI_WEB, DI_VERBOSE, DI_INFO } DISPLAY; typedef struct { DISPLAY port_kind; char *port_name; } PORT_DEF; typedef struct { int port; DISPLAY kind; char desc[64]; char extra[64]; } PORT; typedef struct { long addr; } EXCLUDE; /* some stuff from struct.h */ #define PFREE 0 #define POUTFIT 1 #define PALIVE 2 #define PEXPLODE 3 #define PDEAD 4 #define MAX_STATUS 5 #define PTQUEUE 5 /* Paradise servers only */ /* function prototypes */ extern void init_connections(/*void*/), main_loop(/*void*/), Uprintf(/*pseudo-var*/); extern char *ctime(), *n_to_a(/*long*/); extern long lookup(/*char *host, char *iphost*/); extern int wakeup(/*signal handler*/); extern int *sort_servers(/*void*/); void sig_child(/*signal handler*/); /* the child reaper */ /* global variables */ extern int verbose, wait_noconn, wait_empty, wait_open, wait_queue, try_alt_port, server_count, port_count, exclude_count, log_dns_names, new_conn, mnew_conn, busy, busy_time; extern char *prog, location[], admin[]; extern char *classes[], *pclasses[], teamlet[], shipnos[]; extern SERVER *servers, prev_info; extern USER *users; extern PORT_DEF port_defs[]; extern PORT *ports; extern EXCLUDE *excludes; extern time_t uptime, checktime; --- NEW FILE: metacheck.c --- /* meta.c * * - Nick Trown May 1993 Original Version. - Andy McFadden * May 1993 ? Connect to Metaserver. - BDyess (Paradise) ??? * ug Fixes. Add server type field. - Michael Kellen Jan 1995 Don't * list Paradise Servers. List empty servers. - James Soutter Jan 1995 * Big Parsemeta changes. Included some Paradise Code. Added Known Servers * Option. Added option for metaStatusLevel. Bug Fixes. * - Jonathan Shekter Aug 1995 -- changed to read into buffer in all cases, * use findfile() interface for cachefiles. */ #undef DEBUG #include <limits.h> #include <fcntl.h> #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/time.h> #include <stdlib.h> #include <sys/select.h> #include <strings.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> /* Constants */ #define BUF 6144 #define LINE 80 /* Width of a meta-server * line */ /* Local Types */ #define LONG long struct servers { char address[LINE]; int port; int age; int players; int status; char typeflag; }; struct servers *serverlist = NULL; /* The record for each * server. */ int num_servers = 0; /* The number of servers. */ char *metaWindowName; /* The window's name. */ char metaserver[256] = "metaserver.netrek.org"; int metaport = 3521; /* The status strings: The order of the strings up until statusNull is * important because the meta-client will display all the strings up to a * particular point. * * The strings after statusNull are internal status types and are formatted * separatly from the other strings. * * The string corresponding to "statusNull" is assigned to thoes servers which * have "statusNobody" or earlier strings in old, cached, meta-server data. */ char *statusStrings[] = {"OPEN:", "Wait queue:", "Nobody", "Timed out", "No connection", "Active", "CANNOT CONNECT", "DEFAULT SERVER"}; enum statusTypes { statusOpen = 0, statusWait, statusNobody, statusTout, statusNoConnect, statusNull, statusCantConnect, statusDefault }; const int defaultStatLevel = statusTout; /* Functions */ extern void terminate(int error); static int open_port(char *host, int port, int verbose) /* The connection to the metaserver is by Andy McFadden. This calls the * metaserver and parses the output into something useful */ { struct sockaddr_in addr; struct hostent *hp; int sock; /* Connect to the metaserver */ /* get numeric form */ if ((addr.sin_addr.s_addr = inet_addr(host)) == -1) { if ((hp = gethostbyname(host)) == NULL) { if (verbose) fprintf(stderr, "unknown host '%s'\n", host); return (-1); } else { addr.sin_addr.s_addr = *(LONG *) hp->h_addr; } } addr.sin_family = AF_INET; addr.sin_port = htons(port); if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { if (verbose) perror("socket"); return (-1); } if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (verbose) perror("connect"); close(sock); return (-1); } return (sock); } static void parseInput(char *in, FILE * out, int statusLevel) /* Read the information we need from the meta-server. */ { char line[LINE + 1], *numstr, *point, **statStr; struct servers *slist; int rtn, max_servers; int count; #ifdef DEBUG printf("In parseInput\n"); #endif /* Create some space to hold the entries that will be read. More space can * be added later */ serverlist = (struct servers *) malloc(sizeof(struct servers) * 10); max_servers = 10; num_servers = 0; /* Add the default server */ while (1) { /* Read a line */ point = line; count = LINE + 1; do { if (!(--count)) { fputs("Warning: Line from meta server was too long!!!\n", stderr); ++point; /* Pretend we read a '\n' */ break; } rtn = *in++; if (!rtn) /* use a zero to mark end of buffer */ return; *(point++) = rtn; } while (rtn != EOF && rtn != '\n'); *(--point) = '\0'; if (out != NULL) /* Save a copy of the stuff * we read */ { fputs(line, out); putc('\n', out); } /* Find somewhere to put the information that is just about to be * parsed */ if (num_servers >= max_servers) { max_servers += 5; serverlist = (struct servers *) realloc(serverlist, sizeof(struct servers) * max_servers); } slist = serverlist + num_servers; /* Is this a line we want? */ if (sscanf(line, "-h %s -p %d %d %d", slist->address, &(slist->port), &(slist->age)) != 3) { continue; } /* Find the status of the server, eg "Not responding". */ for (statStr = statusStrings + statusLevel ; statStr >= statusStrings ; --statStr) { if ((numstr = strstr(line, *statStr)) != NULL) { (slist->status) = statStr - statusStrings; (slist->players) = 0; sscanf(numstr, "%*[^0123456789] %d", &(slist->players)); break; } } if (statStr < statusStrings) /* No status was allocated */ continue; /* Read the flags */ slist->typeflag = *(point - 1); /* Don't list Paradise Servers */ if (slist->typeflag != 'P') { #ifdef DEBUG printf("HOST:%-30s PORT:%-6d %12s %-5d %d %c\n", serverlist[num_servers].address, serverlist[num_servers].port, statusStrings[serverlist[num_servers].status], serverlist[num_servers].players, serverlist[num_servers].typeflag); #endif ++num_servers; } } } #define MAXMETABYTES 2048 static int sock; /* the socket to talk to the metaservers */ static int sent = 0; /* number of solicitations sent */ static int seen = 0; /* number of replies seen */ static int ReadMetasSend() { struct sockaddr_in address; /* the address of the metaservers */ int length; /* length of the address */ int bytes; /* number of bytes received from meta' */ fd_set readfds; /* the file descriptor set for select() */ struct timeval timeout; /* timeout for select() call */ char packet[MAXMETABYTES]; /* buffer for packet returned by meta' */ int max_servers; /* number of servers we have defined */ sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { perror("ReadMetasSend: socket"); return 0; } address.sin_addr.s_addr = INADDR_ANY; address.sin_family = AF_INET; address.sin_port = 0; if (bind(sock,(struct sockaddr *)&address, sizeof(address)) < 0) { perror("ReadMetasSend: bind"); return 0; } address.sin_family = AF_INET; address.sin_port = htons(metaport); /* attempt numeric translation first */ if ((address.sin_addr.s_addr = inet_addr(metaserver)) == -1) { struct hostent *hp; /* then translation by name */ if ((hp = gethostbyname(metaserver)) == NULL) { /* if it didn't work, return failure and warning */ fprintf(stderr, "Cannot resolve metaserver address of %s, check for DNS problems?\n", metaserver); return 0; } else { int i; /* resolution worked, send a query to every metaserver listed */ for(i=0;;i++) { /* check for end of list of addresses */ if (hp->h_addr_list[i] == NULL) break; address.sin_addr.s_addr = *(long *) hp->h_addr_list[i]; printf("Requesting player list from metaserver at %s\n", inet_ntoa(address.sin_addr)); if (sendto(sock, "?", 1, 0, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("ReadMetasSend: sendto"); return 0; } sent++; } } } else { /* send only to the metaserver specified by numeric IP address */ if (sendto(sock, "?", 1, 0, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("ReadMetasSend: sendto"); return 0; } sent++; } return 1; } static int ReadMetasRecv() { struct sockaddr_in address; /* the address of the metaservers */ int length; /* length of the address */ int bytes; /* number of bytes received from meta' */ fd_set readfds; /* the file descriptor set for select() */ struct timeval timeout; /* timeout for select() call */ char packet[MAXMETABYTES]; /* buffer for packet returned by meta' */ int max_servers; /* number of servers we have defined */ /* now await and process replies */ while(1) { char *p; int servers, i, j; FD_ZERO(&readfds); FD_SET(sock, &readfds); timeout.tv_sec=0; timeout.tv_usec=1000; if (select(FD_SETSIZE, &readfds, NULL, NULL, &timeout) < 0) { perror("ReadMetasRecv: select"); return 0; } /* if the wait timed out, then we give up */ if (!FD_ISSET(sock, &readfds)) { /* here placeth logic that sees time for reply is up and asks again */ return 0; } /* so we have data back from a metaserver */ length = sizeof(address); bytes = recvfrom(sock, packet, MAXMETABYTES, 0, (struct sockaddr *)&address, &length ); if (bytes < 0) { perror("ReadMetasRecv: recvfrom"); fprintf(stderr, "sock=%d packet=%08.8x length=%d\n", sock, packet, MAXMETABYTES ); return 0; } /* terminate the packet received */ packet[bytes] = 0; printf("\n%s\n", packet); /* process the packet, updating our server list */ /* version identifier */ p = strtok(packet,","); if (p == NULL) continue; if (p[0] != 'r') continue; /* number of servers */ p = strtok(NULL,"\n"); if (p == NULL) continue; servers = atoi(p); /* sanity check on number of servers */ if (servers > 2048) continue; if (servers < 0) continue; printf("\nMetaserver at %s responded with %d server%s\n", inet_ntoa(address.sin_addr), servers, servers == 1 ? "" : "s" ); /* allocate or extend a server list */ if (serverlist == NULL) { serverlist = (struct servers *) malloc( sizeof(struct servers) * servers); } else { serverlist = (struct servers *) realloc(serverlist, sizeof(struct servers) * ( servers + num_servers )); } /* for each server listed by this metaserver packet */ for(i=0;i<servers;i++) { struct servers *sp = serverlist; char *address, type; int port, status, age, players, queue; address = strtok(NULL,","); /* hostname */ if (address == NULL) continue; p = strtok(NULL,","); /* port */ if (p == NULL) continue; port = atoi(p); p = strtok(NULL,","); /* status */ if (p == NULL) continue; status = atoi(p); /* ignore servers here based on status */ p = strtok(NULL,","); /* age (of data in seconds) */ if (p == NULL) continue; age = atoi(p); p = strtok(NULL,","); /* players */ if (p == NULL) continue; players = atoi(p); p = strtok(NULL,","); /* queue size */ if (p == NULL) continue; queue = atoi(p); p = strtok(NULL,"\n"); /* server type */ if (p == NULL) continue; type = p[0]; /* find in current server list? */ for(j=0;j<num_servers;j++) { sp = serverlist + j; if ((!strcmp(sp->address,address)) && (sp->port == port)) { fprintf(stderr, "%s:%d already listed\n", address, port); break; } } /* if it was found, check age */ if (j != num_servers) { if (age > sp->age) continue; } else { /* not found, store it at the end of the list */ sp = serverlist + j; strncpy(sp->address,address,LINE); sp->port = port; sp->age = age; num_servers++; } /* from meta.h of metaserver code */ #define SS_WORKING 0 #define SS_QUEUE 1 #define SS_OPEN 2 #define SS_EMPTY 3 #define SS_NOCONN 4 #define SS_INIT 5 switch (status) { case SS_QUEUE: sp->status = statusWait; sp->players = queue; break; case SS_OPEN: sp->status = statusOpen; sp->players = players; break; case SS_EMPTY: sp->status = statusNobody; sp->players = 0; break; case SS_NOCONN: /* no connection */ case SS_WORKING: /* metaserver should not return this */ case SS_INIT: /* nor this */ default: sp->status = statusNoConnect; sp->players = 0; break; } sp->typeflag = type; } /* finished processing the packet */ /* count the number of replies we have received */ seen++; /* if we have seen the same number of replies to what we sent, end */ if (sent == seen) return 1; } } static int ReadFromMeta(int statusLevel, int parse) /* Read from the meta-server. Return TRUE on success and FALSE on failure. */ { FILE *in, *out; char *cacheName; char cacheFileName[PATH_MAX]; char tmpFileName[PATH_MAX]; char *sockbuf, *buf; int bufleft=BUF-1; int len; int sock; if ((sock = open_port(metaserver, metaport, 1)) <= 0) { fprintf(stderr, "Cannot connect to MetaServer (%s , %d)\n", metaserver, metaport); return 0; } /* Allocate a buffer and read until full */ buf = sockbuf = (char *)malloc(BUF); while (bufleft > 0 && (len = read(sock, buf, bufleft)) > 0) { bufleft-=len; buf += len; printf("Read %d bytes from Metaserver\n", len); } close (sock); *buf = 0; /* End of buffer marker */ if (len<0) { perror ("read"); free(sockbuf); return 0; } printf("Got this from a TCP request from metaserver (unparsed)\n%s", sockbuf); if(parse) parseInput(sockbuf, out, statusLevel); free(sockbuf); metaWindowName = "MetaServer List"; return 1; } void parsemeta(int metaType) /* Read and Parse the metaserver information, either from the metaservers * by UDP (1), from a single metaserver by TCP (3), or from the cache (2). * * NOTE: This function sets the variable "num_servers" which is later used to * decide the height of the metaserver window. */ { int statusLevel; statusLevel = 0; if (statusLevel < 0) statusLevel = 0; else if (statusLevel >= statusNull) statusLevel = statusNull - 1; switch (metaType) { case 1: ReadMetasSend(); ReadMetasRecv(); return; break; case 2: if (ReadFromMeta(statusLevel,1)) return; break; case 3: if (ReadFromMeta(statusLevel,1)) return; break; } } static void metarefresh(int i) /* Refresh line i in the list */ { char buf[LINE + 1]; struct servers *slist; slist = serverlist + i; sprintf(buf, "%-40s %14s ", slist->address, statusStrings[slist->status]); if (slist->status <= statusNull) { if (slist->status == statusOpen || slist->status == statusWait) { /* Don't print the number of players if nobody is playing */ sprintf(buf + strlen(buf), "%-5d ", serverlist[i].players); } else { strcat(buf, " "); } switch (serverlist[i].typeflag) { case 'P': strcat(buf, "Paradise"); break; case 'B': strcat(buf, "Bronco"); break; case 'C': strcat(buf, "Chaos"); break; case 'I': strcat(buf, "INL"); break; case 'S': strcat(buf, "Sturgeon"); break; case 'H': strcat(buf, "Hockey"); break; case 'F': strcat(buf, "Dogfight"); break; default: strcat(buf, "Unknown"); break; } } printf("%s\n", buf); } static void metadone(void) /* Unmap the metaWindow */ { /* Unmap window */ free(serverlist); } int main(int argc, char *argv) { int i; printf("Doing a TCP request (only one server) to %s\n",metaserver); ReadFromMeta(0, 0); printf("\nNow doing UDP request (possibly more than one server) to %s\n",metaserver); printf("\nWhat you will see is the IP address(es) of the server(s),\n"); printf(" followed by the raw packet(s) received, \n"); printf(" followed by the parsed (and merged) output\n\n"); ReadMetasSend(); sleep(5); ReadMetasRecv(); for(i=0;i<num_servers;i++) metarefresh(i); } --- NEW FILE: checkalive --- #!/usr/bin/perl open(PS, "/bin/ps ax|grep metaserverII |/bin/grep -v grep|") || die "Error starting ps!\n"; if($ps = <PS>) { exit; } else { print("Starting metaserver\n"); chdir("/home/unbelver/src/metaserver"); exec("./metaserverII -d"); } --- NEW FILE: metarc --- # # Sample configuration for MetaServerII # # $Id: metarc,v 1.1 2006/02/14 06:43:11 unbelver Exp $ # # ports to listen on for user connections # # ----- num --- kind -- "Description" ------------------------- extra-stuff --- #port: 3520 old "Original METASERVER format" port: 3521 new "New and improved format" port: 3522 verbose "Verbose format, with player lists" port: 1080 web "Web browser HTML format" #port: 3523 info "News & information" metaII_info #port: 3524 info "Server FAQ posting (long)" server_faq #port: 3525 info "MetaServerII config file" metarc #port: 3530 info "RSA key list" rsa_keys #port: 3531 info "Server Feature List" features # couldn't connect to server, wait xx minutes wait_noconn: 30 # server is empty, wait xx minutes wait_empty: 15 # server is open (1-15 players), wait xx minutes wait_open: 5 # server has a wait queue, wait xx minutes wait_queue: 10 # check port-1 (player list port) first? try_alt_port: on # shall we do logging? do_logging: on # log DNS hostnames instead of numeric IP addresses? (DNS lookups can cause # pauses during client connections) log_dns_names: off # where are we? location: Earth # who maintains me? admin: metaserver at us.netrek.org # should we write to a checkpoint file before exiting? save_ckp: on # # List of sites on our "bad" list. They get a standard "go away" message # if they try to connect. [actually, they just get dropped] # # Future enhancement, for entire subdomains, allowing tailored messages: # exclude: ip-addr ip-mask message-file # # # List of servers; runs until end of file. MetaServerII will use the IP # address if one is given; if you use 0.0.0.0 it will resolve the domain # name at startup time. # # Note that this format is identical to the FAQ server list; just cut & paste. # If you plan to take MetaServerII up and down frequently, put the most # popular servers near the top, so that they can be scanned right away when # the program comes up. # servers: # Number of Players ---vv # server name ----------------- IP address ---- Port -- Notes (optional) ------ # # BRONCO/VANILLA Servers (B): B RSAnetrek.unh.edu 0.0.0.0 2592 20 New Hampshire. B RSAsoda.csua.berkeley.edu 0.0.0.0 2592 20 Berkeley, CA B RSAspamburger.openface.ca 0.0.0.0 2592 20 Montreal, Canada B RSAse.netrek.org 0.0.0.0 2592 20 Sweden B RSAkirk.hal-pc.org 0.0.0.0 2592 20 # # CHAOS Servers (C): # # # HOCKEY Servers (H): # # H hockey.psychosis.net 140.186.18.198 2592 36 # # PARADISE Servers (P): # P bode.ee.ualberta.ca 0.0.0.0 2592 20 Alberta, Canada. P tanya.ucsd.edu 0.0.0.0 2592 20 San Diego, Ca. P paradise.games.uk.demon.net 0.0.0.0 2592 32 P europa.informatik.uni-frankfurt.de 141.2.20.4 2592 32 P paradise-lost.kulua.org 0.0.0.0 2592 32 # # DOGFIGHT/BASEPRACTICE Servers (F): # # # STURGEON Servers (S): # --- NEW FILE: server_faq --- This is a list of all known public Netrek servers. If you know of any others, or if any of my information is wrong, please mail tom at csua.Berkeley.EDU. Note: All servers are UDP unless noted otherwise. Subject: Vanilla servers in America (short listing) Description: These servers are based on the orignal code, Terence Chang's bronco code, or Nick Trown's New Vanilla release. They are not all identical but they have minimal modifications. Server name INET address Port Notes bronco.ece.cmu.edu 128.2.210.65 2592 Now RSA only. calvin.usc.edu 128.125.62.143 2592 USC, RSA. netrek.cs.mcgill.ca 132.206.51.198 2592 Canada. garnet.cdf.toronto.edu 128.100.31.36 5555 Canada. wormhole.ecst.csuchico.edu 132.241.1.117 5858 Replaces guzzler, RSA. bigbang.astro.indiana.edu 129.79.157.41 2592 Indiana, RSA. rosebud.umiacs.umd.edu 128.8.120.103 2592 Maryland. mean.mu.caltech.edu 131.215.143.6 2592 Caltech, borgish. bradbert.ugcs.caltech.edu 131.215.134.140 2592 Caltech. Subject: Vanilla servers outside America (short listing) Description: Same as above but not in America. peanuts.informatik.uni-tuebingen.de 134.2.14.1 2592 Germany. bayes.ibr.cs.tu-bs.de 134.169.34.33 5855 Germany. legend.cma.fr 192.33.149.2 2592 France, RSA only. netrek.chemietechnik.uni-dortmund.de 129.217.174.20 592 Germany. ghost.dsi.unimi.it 149.132.1.2 2592 Italy, RSA. netrek.cs.bham.ac.uk 147.188.192.10 2592 U.K. RSA only. megalag.risc.uni-linz.ac.at 193.170.38.158 2592 Replaces melmac. fisher.psy.vu.nl 130.37.96.2 2592 Amsterdam. rsls6.sprachlit.uni-regensburg.de 132.199.136.36 2592 Germany. gilean.solace.mh.se 193.10.118.132 2592 Sweeden, experimental. Subject: Sturgeon-style upgrade servers (short listing) Description: These servers are based on Donald Tsang's Sturgeon source. They include ship upgrades, a modified combat system, ridiculous starbases, and more. rosebud.umiacs.umd.edu 128.8.120.103 7654 U Maryland. netrek.engg.ksu.edu 129.130.41.86 3333 Kansas State. Subject: Paradise-style servers (short listing) Description: These servers use the NetrekII/Paradise source. Paradise servers can be configured to many settings, including that simulating a regular bronco server. The source code release includes an expanded galaxy, impluse/warp differentiation, suns, extra ships, and more. netrek.cis.ufl.edu 128.227.224.254 2592 UFL, RSA only. defiant.theo-physik.uni-kiel.de 134.245.67.1 2592 Germany. aedile.icaen.uiowa.edu 128.255.17.38 2592 Iowa. eelpout.micro.umn.edu 128.101.245.6 2592 UMN. fantasia.eng.clemson.edu 130.127.152.72 2592 Clemson netrek.skypoint.net 199.86.32.2 2592 ? netrek.skypoint.net 199.86.32.2 3333 No warp. fred.cs.city.au.uk 138.40.91.2 2592 UK. netrek.engg.ksu.edu 129.130.41.86 2592 Kansas State. europa.informatik.uni-frankfurt.de 141.2.5.3 2592 Germany. i12.msi.umn.edu 128.101.27.62 2592 Minnisota. cassius.cs.uiuc.edu 128.174.240.3 2592 Illinois. Subject: Hockey servers (short listing) Description: Netrek hockey mode. Most fun you can have on ice without actually being on it. Teams of players try to shoot (pressor) the Puck (a robot) into the other teams goal. hockey.ksu.edu 129.130.6.10 2592 Kansas State. rosebud.umiacs.umd.edu 128.8.120.103 6666 U Maryland. hot.caltech.edu 131.215.9.49 2592 Caltech. Subject: KSU Chaos servers (short listing) Description: These are servers based on the original KSU Chaos mods. They include galaxy-class ships, high refuel rates, free plasmas, wrap-around walls, and much more. netrek.engg.ksu.edu 129.130.41.86 5855 Kansas State. Subject: Dogfight servers (short listing) Description: These are servers designed with dogfighting tournaments in mind. The code is still rather buggy. rosebud.umiacs.umd.edu 128.8.120.103 3333 U Maryland. Comments and server configurations: NOTE: It is an excellent idea to read the MOTD when you first log on to a server (use 'f' and 'b' to move forwards and backwards through the news). Subject: Vanilla servers in America (long listing) bronco.ece.cmu.edu: Server source: Guzzler source. UDP: version 1.0. Hours: 5PM-9AM Eastern time Monday-Friday, all day on weekends. T-mode: 5 on 5. Maintainer: netrek at bronco.ece.cmu.edu Comments: This server is down most of the time. calvin.usc.edu Server source: Scam source. Hours: Open 24 hours. Cyborgs: Never allowed, blessed binary required. Maintainer: hadley at ics.uci.edu Comments: Jacked-up AS's and transwarp. netrek.cs.mcgill.ca Server source: Guzzler source. Hours: Open 24 hours. Cyborgs: Never allowed, RSA client required. Maintainer: kent at cs.mcgill.ca Comments: This server requires RSA. Suppots short packets, ping reporting. Clue checking 3PM-10PM weekdays, all day on weekends. garnet.cdf.toronto.edu Server source: Guzzler source. Hours: Open 24 hours. wormhole.ecst.csuchico.edu Server source: Guzzler source. Hours: Open 24 hours. Cyborgs: Never allowed, RSA client required. Maintainer: trown at ecst.csuchico.edu. Comments: This server requires RSA. Now has the calvin database. Supports short packets, ping reporting, and RCD. Clue checking 5PM-6AM weeknights, all day on weekends. bigbang.astro.indiana.edu Server source: Guzzler source. Hours: Open 24 hours. Cyborgs: Never allowed, RSA client required. Maintainer: netrek at bigbang.astro.indiana.edu Comments: Playerlist on port 2591. finger netrek at bigbang.astro.indiana.edu gives a high score list. Ship stats modified to weaken cruisers. rosebud.umiacs.umd.edu (2592) Server source: Guzzler source. Hours: Open 24 hours. Maintainer: ctso at umiacs.umd.edu Comments: Has the UML player database. Note port number; port 6666 has a hockey server. mean.mu.caltech.edu Server source: Guzzler source. Hours: Open 5PM to 8AM weekdays, all day on weekends. Cyborgs: Encouraged, no binary checking. Maintainer: kantner at hot.caltech.edu tolstoy.afit.af.mil: This server is apparently down. smurfland.cit.buffalo.edu: This server is apparently down. bradbert.ugcs.caltech.edu Server source: Guzzler source. Hours: Open 24 hours. Subject: Vanilla servers outside America (long listing) peanuts.informatik.uni-tuebingen.de Server source: Bronco source. Hours: 5PM-7AM Monday-Friday, all day on weekends. Cyborgs: Allowed. Maintainer: jw at peanuts.informatik.uni-tuebingen.de. bayes.ibr.cs.tu-bs.de Server source: Bronco source. Hours: Open 24 hours. Cyborgs: Always allowed. Maintainer: schrei at ibr.cs.tu-bs.de. legend.cma.fr Server source: Guzzler source. Hours: Open 24 hours. Cyborgs: Never allowed, RSA client required. Maintainer: jmt at cma.cma.fr. Comments: Requires message reading. Supports short packets and RC_DISTRESS. netrek.chemietechnik.uni-dortmund.de Server source: Bronco source. Hours: Closed 8 AM to noon Monday-Friday. Maintainer: hw at plato.Chemietechnik.Uni-Dortmund.DE. Comments: No Iggy. ghost.dsi.unimi.it Server source: Bronco source. Hours: Open 5PM-10AM Monday-Friday, all day on weekends. Cyborgs: Always allowed. Maintainer: carlo at ghost.dsi.unimi.it. netrek.cs.bham.ac.uk Server source: Bronco source. Hours: 5PM to 8AM Monday-Friday, all day weekends. T-mode: 4 on 4. Cyborgs: Never allowed, RSA client required. Maintainer: A.Willams at cs.bham.ac.uk. megalag.risc.uni-linz.ac.at Server source: Guzzler source. Hours: Open 24 hours. T-mode: 3 on 3. Cyborgs: Never allowed, RSA client required. Maintainer: siegl at risc.uni-linz.ac.at. Comments: Usually runs in base practice server mode. Suppots short packets and RC_DISTRESS. Replaces melmac. fisher.psy.vu.nl Server source: Guzzler source. Hours: Open 24 hours. T-mode: 4 on 4. Cyborgs: Never allowed, RSA client required. Maintainer: rob at psy.vu.nl. Comments: Clue hours 5PM to 8PM local time. gilean.solace.mh.se Server source: Guzzler source. Hours: Open 24 hours. T-mode: 3 on 3. Cyborgs: Never allowed, RSA client required. Maintainer: netrek at solace.mh.se. Comments: Experimental server based on Sturgeon mods. This server was down at the time of testing. Subject: Sturgeon-style upgrade servers (long listing) rosebud.umiacs.umd.edu: This server is apparently down. netrek.engg.ksu.edu: This server is apparently down. Subject: Paradise-style servers (long listing) netrek.cis.ufl.edu Server source: Paradise source. Hours: Open 24 hours. Maintainer: thoth at aviator.cis.ufl.edu. Comments: This is Arctica, another branch of the Paradise line. defiant.theo-physik.uni-kiel.de Server source: Paradise source. Hours: Open 24 hours. Maintainer: trek at theo-physik.uni-kiel.de. Comments: Good round-trip times. Replaces ravel. aedile.icaen.uiowa.edu Server source: Paradise source. Hours: Open 24 hours. eelpout.micro.umn.edu: This server is apparently down. fantasia.eng.clemson.edu Server source: Paradise source. Hours: Open 24 hours. Maintainer: ttatum at harley.eng.clemson.edu, dcrane at eng.clemson.edu. This server was down at the time of testing. netrek.skypoint.net: This server is apparently down. fred.cs.city.ac.uk Server source: Paradise source. Hours: Open 24 hours. Maintainer: deorth at city.ac.uk. This server was down at the time of testing. netrek.engg.ksu.edu (2592) Server source: Paradise source. Hours: Open 24 hours. Maintainer: tundra at cis.ksu.edu. Comments: This is Armageddon, a mixture of Paradise and KSU Chaos. europa.informatik.uni-frankfurt.de Server source: Paradise source. Hours: Open 24 hours. i12.msi.umn.edu Server source: Paradise source. Hours: Open 24 hours. This server was down at the time of testing. cassius.cs.uiuc.edu Server source: Paradise source. Hours: Open 24 hours. Subject: Hockey servers (long listing) hockey.ksu.edu Server source: Guzzler source, hockey mode. Hours: Open 24 hours. Cyborgs: Allowed. Maintainer: bav at hobbes.ksu.ksu.edu. Comments: Hockey mode all the time, send 'help' to player g for details. rosebud.umiacs.umd.edu (6666) Server source: Guzzler source, hockey mode. Hours: Open 24 hours. Cyborgs: Never allowed, RSA client required. Maintainer: ctso at umiacs.umd.edu. Comments: Note port number, normal netrek server runing on port 2592. hot.caltech.edu Server source: Guzzler source, hockey mode. Hours: Open 5PM-8AM weekdays, all day weekends. Maintainer: kantner at hot.caltech.edu rsls6.sprachlit.uni-regensburg.de Server source: Guzzler source, hockey mode. Hours: Open 24 hours. Subject: KSU Chaos servers (long listing) netrek.engg.ksu.edu (2592) Server source: KSU Chaos. Hours: Open 24 hours. Cyborgs: Allowed. Maintainer: greyhelm at engg.ksu.edu. Comments: KSU Chaos with grit-style robots. Subject: Dogfight servers (long listing) rosebud.umiacs.umd.edu (3333) Server source: Guzzler source, dogfight mode. Hours: Open 24 hours. Cyborgs: Not allowed, RSA client required. Maintainer: ctso at umiacs.umd.edu. Comments: Note port number; normal netrek on port 2592, hockey on port 6666. This server was down at the time of testing. --- NEW FILE: server.c --- /* * server.c - interact with a Netrek server * * MetaServerII * Copyright (c) 1993 by Andy McFadden * * This is sorta messed up. Things would be much simpler if we could just * sit and block on the Netrek socket, but that could take several seconds * to complete. We need something which works the same way, but deals with * partial packets, etc, etc, without blowing away the CPU like we are now. */ #include <stdio.h> #include <fcntl.h> #include <string.h> /* or strings.h */ #include <ctype.h> #include <sys/types.h> #include <sys/time.h> /* #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> */ #include <netinet/in.h> #include <errno.h> #include "meta.h" #include "packets.h" int new_conn; /* scan.c sets to TRUE when server conn is opened */ int mnew_conn; /* whoops, need one for read_minus too */ /* * Private stuff */ #define NOBODY_PLAYING "No one is playing" #define SERVER_IN_USE "Server is in use" #define BUFSIZE 85000 /* was 65000 */ static char rdbuf[BUFSIZE+1]; static int ssock; /* from struct.h */ char *classes[NUM_TYPES] = { "SC", "DD", "CA", "BB", "AS", "SB", "GA", "??" }; char *pclasses[PNUM_TYPES] = { "SC", "DD", "CA", "BB", "AS", "SB", "AT", "JS", "FR", "WB", "CL", "CV", "UT", "PT" }; char teamlet[] = { 'I', 'F', 'R', 'X', 'K', 'X', 'X', 'X', 'O' }; char shipnos[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; #define NUMRANKS 9 #define PNUMRANKS 18 static struct rankstrings { char *name; int len; } rank[NUMRANKS] = { { "Ensign", 6 }, { "Lieutenant", 9 }, /* one short in case it got cut off */ { "Lt. Cmdr.", 9 }, { "Commander", 9 }, { "Captain", 7 }, { "Flt. Capt.", 9 }, /* (ditto) */ { "Commodore", 9 }, { "Rear Adm.", 9 }, { "Admiral", 7 }, /* "(unknown)" */ }, prank[PNUMRANKS] = { { "Recruit", 7 }, { "Specialist", 9 }, { "Cadet", 5 }, { "Midshipman", 9 }, { "Ensn., J.G.", 9 }, { "Ensign", 6 }, { "Lt., J.G.", 9 }, { "Lieutenant", 9 }, { "Lt. Cmdr.", 9 }, { "Commander", 9 }, { "Captain", 7 }, { "Fleet Capt.", 9 }, { "Commodore", 9 }, { "Moff", 4 }, { "Grand Moff", 9 }, { "Rear Adml.", 9 }, { "Admiral", 7 }, { "Grand Adml.", 9 } }; /* * --------------------------------------------------------------------------- * Stuff adapted from netrek/socket.c * --------------------------------------------------------------------------- */ static foo() {} struct packet_handler handlers[] = { { 0, NULL }, /* record 0 */ { sizeof(struct mesg_spacket), foo }, /* SP_MESSAGE */ { sizeof(struct plyr_info_spacket), foo }, /* SP_PLAYER_INFO */ { sizeof(struct kills_spacket), foo }, /* SP_KILLS */ { sizeof(struct player_spacket), foo }, /* SP_PLAYER */ { sizeof(struct torp_info_spacket), foo }, /* SP_TORP_INFO */ { sizeof(struct torp_spacket), foo }, /* SP_TORP */ { sizeof(struct phaser_spacket), foo }, /* SP_PHASER */ { sizeof(struct plasma_info_spacket), foo}, /* SP_PLASMA_INFO */ { sizeof(struct plasma_spacket), foo}, /* SP_PLASMA */ { sizeof(struct warning_spacket), foo }, /* SP_WARNING */ { sizeof(struct motd_spacket), foo }, /* SP_MOTD */ { sizeof(struct you_spacket), foo }, /* SP_YOU */ { sizeof(struct queue_spacket), foo }, /* SP_QUEUE */ { sizeof(struct status_spacket), foo }, /* SP_STATUS */ { sizeof(struct planet_spacket), foo }, /* SP_PLANET */ { sizeof(struct pickok_spacket), foo }, /* SP_PICKOK */ { sizeof(struct login_spacket), foo }, /* SP_LOGIN */ { sizeof(struct flags_spacket), foo }, /* SP_FLAGS */ { sizeof(struct mask_spacket), foo }, /* SP_MASK */ { sizeof(struct pstatus_spacket), foo }, /* SP_PSTATUS */ { sizeof(struct badversion_spacket), foo }, /* SP_BADVERSION */ { sizeof(struct hostile_spacket), foo }, /* SP_HOSTILE */ { sizeof(struct stats_spacket), foo }, /* SP_STATS */ { sizeof(struct plyr_login_spacket), foo }, /* SP_PL_LOGIN */ { sizeof(struct reserved_spacket), foo }, /* SP_RESERVED */ { sizeof(struct planet_loc_spacket), foo }, /* SP_PLANET_LOC */ /* paradise packets */ { sizeof(struct scan_spacket), foo }, /* SP_SCAN (ATM) */ { sizeof(struct udp_reply_spacket), foo }, /* SP_UDP_STAT */ { sizeof(struct sequence_spacket), foo }, /* SP_SEQUENCE */ { sizeof(struct sc_sequence_spacket), foo },/*SP_SC_SEQUENCE*/ { sizeof(struct rsa_key_spacket), foo }, /* SP_RSA_KEY */ { sizeof(struct motd_pic_spacket), foo }, /* SP_MOTD_PIC */ { sizeof(struct stats_spacket2), foo }, /* SP_STATS2 */ { sizeof(struct status_spacket2), foo }, /* SP_STATUS2 */ { sizeof(struct planet_spacket2), foo }, /* SP_PLANET2 */ { sizeof(struct obvious_packet), foo }, /* SP_TEMP_5 */ { sizeof(struct thingy_spacket), foo }, /* SP_THINGY */ { sizeof(struct thingy_info_spacket), foo },/* SP_THINGY_INFO*/ { sizeof(struct ship_cap_spacket), foo } /* SP_SHIP_CAP */ }; int sizes[] = { 0, /* record 0 */ sizeof(struct mesg_cpacket), /* CP_MESSAGE */ sizeof(struct speed_cpacket), /* CP_SPEED */ sizeof(struct dir_cpacket), /* CP_DIRECTION */ sizeof(struct phaser_cpacket), /* CP_PHASER */ sizeof(struct plasma_cpacket), /* CP_PLASMA */ sizeof(struct torp_cpacket), /* CP_TORP */ sizeof(struct quit_cpacket), /* CP_QUIT */ sizeof(struct login_cpacket), /* CP_LOGIN */ sizeof(struct outfit_cpacket), /* CP_OUTFIT */ sizeof(struct war_cpacket), /* CP_WAR */ sizeof(struct practr_cpacket), /* CP_PRACTR */ sizeof(struct shield_cpacket), /* CP_SHIELD */ sizeof(struct repair_cpacket), /* CP_REPAIR */ sizeof(struct orbit_cpacket), /* CP_ORBIT */ sizeof(struct planlock_cpacket), /* CP_PLANLOCK */ sizeof(struct playlock_cpacket), /* CP_PLAYLOCK */ sizeof(struct bomb_cpacket), /* CP_BOMB */ sizeof(struct beam_cpacket), /* CP_BEAM */ sizeof(struct cloak_cpacket), /* CP_CLOAK */ sizeof(struct det_torps_cpacket), /* CP_DET_TORPS */ sizeof(struct det_mytorp_cpacket), /* CP_DET_MYTORP */ sizeof(struct copilot_cpacket), /* CP_COPILOT */ sizeof(struct refit_cpacket), /* CP_REFIT */ sizeof(struct tractor_cpacket), /* CP_TRACTOR */ sizeof(struct repress_cpacket), /* CP_REPRESS */ sizeof(struct coup_cpacket), /* CP_COUP */ sizeof(struct socket_cpacket), /* CP_SOCKET */ sizeof(struct options_cpacket), /* CP_OPTIONS */ sizeof(struct bye_cpacket), /* CP_BYE */ sizeof(struct dockperm_cpacket), /* CP_DOCKPERM */ sizeof(struct updates_cpacket), /* CP_UPDATES */ sizeof(struct resetstats_cpacket), /* CP_RESETSTATS */ sizeof(struct reserved_cpacket) /* CP_RESERVED */ }; #define NUM_PACKETS (sizeof(handlers) / sizeof(handlers[0]) - 1) #define NUM_SIZES (sizeof(sizes) / sizeof(sizes[0]) - 1) int sendServerPacket(packet) struct player_spacket *packet; { int size; #ifdef DEBUG printf("- Sending %d\n", packet->type); #endif if (packet->type<1 || packet->type>NUM_SIZES || sizes[packet->type]==0) { fprintf(stderr, "Attempt to send strange packet %d!\n", packet->type); return (-1); } size=sizes[packet->type]; if (gwrite(ssock, packet, size) != size) { return (-1); } return (0); } /*static*/ int gwrite(fd, buf, bytes) int fd; char *buf; register int bytes; { long orig = bytes; register long n; while (bytes) { n = write(fd, buf, bytes); if (n < 0) return(-1); bytes -= n; buf += n; } return(orig); } /* * --------------------------------------------------------------------------- * New stuff * --------------------------------------------------------------------------- */ /*static*/ char * scan_buf(buf, str, count) register char *buf, *str; int count; { register int i; int len, max; len = strlen(str); max = count - len; if (max <= 0) return (NULL); for (i = 0; i < max; i++, buf++) { if (*buf == *str && !strncmp(buf, str, len)) return (buf); } return (NULL); } /* * Parse a "players" output (port 2591 stuff). Returns the #of players found. * * Note that the data here will be overwritten if we get better info from * the normal port (this is useful because it will get player info even * if there's a wait queue). * * Note that we want to do this BEFORE trying the other port, so that we * don't wake up the server if nobody is playing. * * New strategy: look for "standard keywords" in a header line, which is * expected to immediately follow after "<>==". Gather a collection of * line offsets, and use those when scanning the "info" lines. * * Info line strategy: find a line which starts with F/K/R/O/I and a number * or small letter, and grab info until we hit something which doesn't match * (like "<>=="). Then we're done. * * We do have something of a dilemma here: we want to allow the display * to be off by a char or two, but it's possible that one or more of the * fields could be blank (for example, if the user is just logging in). * Until somebody defines the exact format to use (I did ask!), this will * be satisfactory. */ #define PL_SEP "<>==" #ifdef FUBAR #define POS_MAPCHARS 2 #define POS_PSEUDO POS_MAPCHARS+4 #define POS_LOGIN POS_PSEUDO+17 #define POS_DISPLAY POS_LOGIN+12 #define POS_SHIP POS_DISPLAY+18 #endif #define POSx_MAPCHARS 0 #define POSx_RANK 1 #define POSx_PSEUDO 2 #define POSx_LOGIN 3 #define POSx_DISPLAY 4 #define POSx_TYPE 5 #define MAX_POSx 6 #define TEAMCHARS "IFRKO" #define PLAYERCHARS "0123456789abcdefghiABCDEFGHI" struct { int offset; char *title; int len; } offsets[MAX_POSx] = { { 0, "Pl:", 3 }, { 0, "Rank", 4 }, { 0, "Name", 4 }, { 0, "Login", 5 }, { 0, "Display", 7 }, { 0, "Type", 4 }, }; /*static*/ int parse_playerlist(buf, size) register char *buf; int size; { SERVER *sp = &servers[busy]; PLAYER *pp; int pl_count = 0; register char *end = buf+size; char ship_type[4], *cp, *brkp; char c; int i, j, pl, len, tmpo; #ifdef DEBUG printf("parsing playerlist, buf = 0x%.8lx, end = 0x%.8lx\n", buf, end); /* DEBUG */ #endif for (i = 0; i < MAX_POSx; i++) offsets[i].offset = -1; /* find start of interesting stuff */ while (buf < end) { if (!strncmp(buf, PL_SEP, strlen(PL_SEP))) break; while (*buf != '\n' && buf < end) buf++; buf++; } /* title line is expected next */ while (*buf != '\n' && buf < end) buf++; buf++; if (buf >= end) return (-1); for (cp = buf; *cp != '\n' && cp < end; cp++) ; if (cp >= end) return (-1); brkp = cp; *brkp = '\0'; /* title line is at *buf; figure out where each of the fields begins */ if ((len = strlen(buf)) < 4) return (-1); for (i = 0, cp = buf; i < len; i++, cp++) { for (j = 0; j < MAX_POSx; j++) { if (*cp == *(offsets[j].title)) { if (!strncmp(cp, offsets[j].title, offsets[j].len)) { /*printf("offset %d (%s) = %d\n", j, offsets[j].title, i);*/ offsets[j].offset = i; i += offsets[j].len; cp += offsets[j].len; break; /* out of j-loop */ } } } } /* resume past the '\0' */ buf = brkp+1; while (buf < end) { tmpo = offsets[POSx_MAPCHARS].offset; if (strchr(TEAMCHARS,*(buf+tmpo)) && strchr(PLAYERCHARS,*(buf+tmpo+1))){ /* found the start */ while (buf < end) { tmpo = offsets[POSx_MAPCHARS].offset; c = *(buf+tmpo+1); if (c >= '0' && c <= '9') pl = c - '0'; else { if (islower(c)) c = toupper(c); pl = (c - 'A') + 10; } if (pl < 0 || pl >= sp->max_players) { /*printf("illegal player #%d\n", pl);*/ return (-1); } /*printf("Player %d\n", pl);*/ pp = &(sp->players[pl]); pp->p_mapchars[0] = c = *(buf+tmpo); pp->p_mapchars[1] = *(buf+tmpo+1); switch (c) { case 'I': pp->p_team = 0; break; case 'F': pp->p_team = 1; break; case 'R': pp->p_team = 2; break; case 'K': pp->p_team = 4; break; case 'O': pp->p_team = 8; break; default: /*printf("Bad Team %s\n", buf);*/ return (-1); } if ((tmpo = offsets[POSx_RANK].offset) == -1 || *(buf+tmpo) == ' ') { pp->p_rank = NUMRANKS; } else { struct rankstrings *ranklist; int nranks; if (sp->type[0]=='P') { nranks = PNUMRANKS; ranklist = prank; } else { nranks = NUMRANKS; ranklist = rank; } pp->p_rank = nranks; for (i = 0; i < nranks; i++) { if (!strncmp(buf+tmpo, ranklist[i].name, ranklist[i].len)) { pp->p_rank = i; break; } } } if ((tmpo = offsets[POSx_PSEUDO].offset) == -1 || *(buf+tmpo) == ' ') { strcpy(pp->p_name, "(none)"); } else { strncpy(pp->p_name, buf+tmpo, 15); cp = pp->p_name + strlen(pp->p_name) -1; if (cp > pp->p_name) { while (*cp == ' ') cp--; *(cp+1) = '\0'; } /*printf("name = '%s' ", pp->p_name);*/ } if ((tmpo = offsets[POSx_LOGIN].offset) == -1 || *(buf+tmpo) == ' ') { strcpy(pp->p_login, "(none)"); } else { if (strtok(buf+tmpo, " ") == NULL) { /*printf("Bad login %s\n", buf);*/ return (-1); } strncpy(pp->p_login, buf+tmpo, 15); *(pp->p_login+15) = '\0'; /*printf("login = '%s' ", pp->p_login);*/ } if ((tmpo = offsets[POSx_DISPLAY].offset) == -1 || *(buf+tmpo) == ' ') { strcpy(pp->p_name, "(none)"); } else { if (strtok(buf+tmpo, " ") == NULL) { /*printf("Bad display %s\n", buf);*/ return (-1); } strncpy(pp->p_monitor, buf+tmpo, 15); *(pp->p_name+15) = '\0'; /*printf("disp = '%s'\n", pp->p_monitor);*/ } if ((tmpo = offsets[POSx_TYPE].offset) == -1) { strcpy(ship_type, "??"); pp->ship_type = NUM_TYPES-1; goto skip_type; } if (*(buf+tmpo) == ' ') tmpo++; /* allow a blank here */ if (*(buf+tmpo) == ' ' || *(buf+tmpo+1) == ' ') { /*printf("Bad ship type %s\n", buf);*/ return (-1); } ship_type[0] = *(buf+tmpo); ship_type[1] = *(buf+tmpo+1); ship_type[2] = '\0'; pp->ship_type = NUM_TYPES-1; /* default is "??" */ for (i = 0; i < NUM_TYPES; i++) { if (!strcmp(ship_type, classes[i])) { pp->ship_type = i; break; } } skip_type: /*printf("ship type = %d (%s)\n", pp->ship_type, classes[pp->ship_type]);*/ pp->p_status = PALIVE; pl_count++; /* move to start of next line */ while (*buf != '\n' && buf < end) buf++; buf++; /* done yet? */ if (*buf == *PL_SEP) goto done; } } while (*buf != '\n' && buf < end) buf++; buf++; } done: sp->player_count = pl_count; return (pl_count); } /* * Read & interpret data from port-1 (sends a player listing). Uses blocking * reads; the amount of data should be small enough that this isn't a problem. * * Returns -1 on error, -2 if not done yet, status otherwise. * * NOTE: if the output of port 2591 was garbage, we would get "0 players * playing" from parse_playerlist(). However, if nobody was playing, then * we should've seen "No one is playing" earlier. So, if we get 0 back from * parse_playerlist(), assume something went wrong and return (-1). */ SERVER_STATUS read_minus(sock) int sock; { static int count = 0; int cc, pc; #ifdef DEBUG printf("Reading fd %d\n", sock); #endif ssock = sock; if (mnew_conn) { count = 0; mnew_conn = FALSE; #ifdef CONNECT /* see if we're really connected */ if ((cc = write(sock, " ", 1)) < 0) { if (errno != EPIPE) { if (verbose) perror("strange read_minus error"); log_msg("strange read_minus write error %d with %s", errno, servers[busy].hostname); } /* no change to why_dead for read_minus */ return (-1); } #endif return (-2); } /* if we get an error, assume it was the server hanging up on us */ if ((cc = read(sock, rdbuf+count, BUFSIZE-count)) < 0) { if (errno != ECONNREFUSED) { if (verbose) perror("read minus"); log_msg("read error %d in read_minus with %s", errno, servers[busy].hostname); } return (-1); } count += cc; if (cc) return (-2); /* more to go (ok if buf fills) */ if (!count) return (-1); /* no info! */ rdbuf[count] = '\0'; /* see if nobody is playing */ if (scan_buf(rdbuf, NOBODY_PLAYING, count) != NULL) { #ifdef DEBUG printf("2591 no one is playing!\n"); #endif return (SS_EMPTY); } /* see if server is up, but no verbose info */ if (scan_buf(rdbuf, SERVER_IN_USE, count) != NULL) return (SS_OPEN); /* okay, we got valid stuff, now parse it and stuff it into SERVER struct */ if (!(pc = parse_playerlist(rdbuf, count))) return (-1); /* was SS_EMPTY */ else if (pc < 0) return (-1); else return (SS_OPEN); } /* * See if we have the info for MAX_PLAYERS players in rdbuf. Returns (-1) * if we don't have all the info yet. Returns 0 otherwise. Exits with 0 * if it encounters an SP_QUEUE packet. Returns (-2) if the data is corrupted. * * We count SP_PLAYER because it's the last packet in the group sent for * each player. If the server sends them in a different order than we could * have bogus data for the last player. Since this is what ck_players does, * we break iff everything else breaks. * * To alleviate the CPU load, this keeps track of the byte offset of the * first interesting item in the buffer. That way we don't have to seek * past the motd every time. Whenever the data in the buffer changes (other * than appending new data), call this with buflen=0 to reset the stuff. * * Here's what the profiler showed after half a day of activity: * * %time cumsecs #call ms/call name * 24.9 8.58 17371 0.49 _read * 23.3 16.62 19318 0.42 _select * 5.5 18.51 mcount * 4.8 20.18 24272 0.07 __doprnt * 4.8 21.84 1484 1.12 _connect * 3.6 23.08 17371 0.07 _scan_packets [] * 3.3 24.21 1 1130.00 _main_loop [] * 3.0 25.23 3042 0.34 _close * 2.9 26.23 1472 0.68 _write * 2.0 26.91 176515 0.00 .rem * 1.8 27.54 13274 0.05 _strpbrk * 1.7 28.14 472 1.27 _parse_server [] * * So this routine is still the most CPU-intensive in the program, but it's * relatively insignificant. */ int scan_packets(buflen) int buflen; { register unsigned char *buf; register int pos, type, size; int plcount = 0; static int first_off = 0; /* opt */ int neat; /* opt */ if (!buflen) { /* reset */ first_off = 0; return; } pos = first_off; buf = (unsigned char *) (rdbuf + pos); if (pos > buflen) { log_msg("something whacked in scan_packets"); #ifdef DEBUG printf("scan_packets has initial pos = %d, buflen = %d\n", pos,buflen); #endif return (-1); } neat = 0; while (pos < buflen) { type = (int) *buf; if (type < 1 || type > NUM_PACKETS) { log_msg("rcvd bad packet %d (0x%x) from %s (%d)", type, type, servers[busy].hostname, servers[busy].port); return (-2); } size = handlers[type].size; if (size <= 0) { /* got bogus size, headed for infinite spin */ log_msg("zero size in scan_packets from %s", servers[busy].hostname); return (-2); } if (size+pos > buflen) return (-1); if (type != SP_MOTD) neat++; /* don't advance first_off anymore */ if (type == SP_PLAYER) { SERVER *serv = &servers[busy]; { struct player_spacket *sp; sp = (struct player_spacket *) buf; } plcount++; if (verbose) printf("Got player # %d\n",plcount); if (plcount >= serv->max_players) return (0); } if (type == SP_QUEUE) return (0); buf += size; pos += size; if (!neat) first_off = pos; /* start here next time */ } return (-1); } /* * Parse the info we got from the server. Returns status. */ SERVER_STATUS parse_server(buflen) int buflen; { SERVER *sp = &servers[busy]; PLAYER *pp; struct queue_spacket *qp; struct plyr_login_spacket *plp; struct plyr_info_spacket *pip; struct pstatus_spacket *psp; register char *buf; register int pos; int type, size; int pl_seen = 0; buf = rdbuf, pos = 0; sp->player_count = 0; pp = sp->players; while (pos < buflen && pl_seen < sp->max_players) { type = (int) *buf; size = handlers[type].size; if (size+pos > buflen) { #ifdef DEBUG printf("type = %d, size = %d, pos = %d, buflen = %d\n", type, size, pos, buflen); #endif return (-1); } switch (type) { case SP_QUEUE: qp = (struct queue_spacket *) buf; sp->queue_size = ntohs(qp->pos); return (SS_QUEUE); case SP_PL_LOGIN: plp = (struct plyr_login_spacket *) buf; if (plp->pnum < 0 || plp->pnum >= sp->max_players) break; strncpy(pp[plp->pnum].p_name, plp->name, 16); strncpy(pp[plp->pnum].p_monitor, plp->monitor, 16); strncpy(pp[plp->pnum].p_login, plp->login, 16); pp[plp->pnum].p_rank = plp->rank; break; case SP_PLAYER_INFO: pip = (struct plyr_info_spacket *) buf; if (pip->pnum < 0 || pip->pnum >= sp->max_players) break; pp[pip->pnum].ship_type = pip->shiptype; pp[pip->pnum].p_team = pip->team; pp[pip->pnum].p_mapchars[0] = teamlet[pip->team]; break; case SP_PSTATUS: psp = (struct pstatus_spacket *) buf; if (psp->pnum < 0 || psp->pnum >= sp->max_players) break; pp[psp->pnum].p_mapchars[1] = shipnos[psp->pnum]; if (psp->status >=0 && psp->status < MAX_STATUS) { pp[psp->pnum].p_status = psp->status; } else if (psp->status == PTQUEUE) { /* Paradise server tournament queue */ pp[psp->pnum].p_status = PALIVE; } else { log_msg("rcvd bad status (%d) from %s", psp->status, servers[busy].hostname); pp[psp->pnum].p_status = PFREE; } /* this is kinda weak, but *we* show up as POUTFIT... */ if (psp->status != PFREE && psp->status != POUTFIT) sp->player_count++; pl_seen++; break; #ifdef GET_MASK /* can't be done w/o full login */ case SP_MASK: mp = (struct mask_spacket *) buf; sp->mask = mp->mask; mask_seen++; printf("mask = 0x%x\n", sp->mask); break; #endif case SP_STATS: case SP_PLAYER: case SP_FLAGS: case SP_KILLS: case SP_HOSTILE: default: /* do nothing */ size = size; /* silly compiler */ } buf += size; pos += size; } /* all done */ if (!sp->player_count) return (SS_EMPTY); else return (SS_OPEN); } /* * Read & interpret data from the standard Netrek port. This is a whole lot * of fun, because we don't want to block on the read() calls. * * Basic algorithm is: * while (still hungry && room in buffer) { * read more data * if (we have all of the information for each player) * analyze it and return * else { * if we've got more room * return with -2 so we come back later * else * return with -1 (hopefully this won't happen!) * } * } * * Two possible adjustments: one is to interpret the data as we go along * (but that quickly turns to spaghetti), the other is to filter out the * motd lines and scrunch the buffer up (which should work, but will raise * the CPU load because of all the bcopy() calls). The latter approach will * be implemented, just in case some bozo has a 60K motd. (idea: wait until * the buffer is >50% full before doing any scrunching.) * * Returns -1 on error, -2 if not yet done, status otherwise. */ SERVER_STATUS read_server(sock) int sock; { struct socket_cpacket sockPack; static char *buf; /* buf & count stay across calls */ static int count, extend_time; int cc, res; ssock = sock; if (new_conn) { new_conn = FALSE; buf = rdbuf; extend_time = 0; count = 0; scan_packets(0); /* reset */ sockPack.type=CP_SOCKET; sockPack.socket=htonl(65535); sockPack.version=(char) SOCKVERSION; if (sendServerPacket(&sockPack) < 0) { /* see if server really exists */ if (errno != EPIPE) { perror("strange error on sendServerPacket"); log_msg("strange server write 27 error %d with %s", errno, servers[busy].hostname); } servers[busy].why_dead = WD_CONN; /* connection really failed */ return (-1); } /* the server might not have data to send just yet */ /* (minor improvement: put a select() call here) */ return (-2); } if ((cc = read(sock, buf, BUFSIZE-count)) < 0) { log_msg("server read error %d from %s", errno, servers[busy].hostname); #ifdef DEBUG perror("read_server read"); #endif servers[busy].why_dead = WD_READ; return (-1); } #ifdef DEBUG printf("read, cc=%d\n", cc); #endif if (!cc) { /* we've hit EOF, but we don't have all the data; assume server dead */ #ifdef DEBUG fprintf(stderr, "EOF hit early\n"); #endif if (verbose) log_msg("+ early EOF reading from %s", servers[busy].hostname); servers[busy].why_dead = WD_GARBAGE; return (-1); } buf += cc, count += cc; if (!extend_time && count > 1024) { busy_time += BUSY_EXTEN; /* this could be in the future... */ extend_time++; } /* FIX: add motd scrunch code here */ if ((res = scan_packets(count)) < 0) { if (res == (-2)) { /* corruption */ servers[busy].why_dead = WD_GARBAGE; return (-1); } if (count < BUFSIZE) return (-2); /* get more */ else { if (verbose)fprintf(stderr,"Internal ERROR: read buffer flooded\n"); log_msg("ERROR: read buffer flooded by server %s\n", servers[busy].hostname); return (-1); /* ran out of room! */ } } /* hooray, we have all the info in the buffer! */ return (parse_server(count)); } --- NEW FILE: disp_info.c --- /* * disp_info.c - send a file to the client * * MetaServerII * Copyright (c) 1993 by Andy McFadden * * $Id: disp_info.c,v 1.1 2006/02/14 06:43:11 unbelver Exp $ * */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <errno.h> #include "meta.h" /* * display the info file */ int display_info(idx, port_idx) int idx, port_idx; { if (verbose) printf("%s: sending info '%s'\n", prog, ports[port_idx].extra); send_info(idx, ports[port_idx].extra); return (0); } static int send_info(idx, file) int idx; char *file; { USER *up; FILE *fp; char buf[128]; up = &users[idx]; up->data_size = up->buf_size = up->pos = 0; if ((fp = fopen(file, "r")) == NULL) { if (errno == EMFILE) /* I don't see how this could be happening... weird */ Uprintf(idx, "\nToo many files open here; wait a bit and try again\n\n"); else Uprintf(idx,"\nUnable to open %s (err %d); tell the admin (%s)\n\n", file, errno, admin); log_msg("Unable to open %s (errno=%d)", file, errno); return (0); } while (1) { fgets(buf, 128, fp); if (ferror(fp)) log_msg("error reading %s: %d", file, errno); if (ferror(fp) || feof(fp)) break; Uprintf(idx, "%s", buf); } fclose(fp); return (0); } --- NEW FILE: main.c --- /* * main.c - args, configuration, logging * * MetaServerII * Copyright (c) 1993 by Andy McFadden * * $Id: main.c,v 1.1 2006/02/14 06:43:11 unbelver Exp $ * */ #include <stdio.h> #include <string.h> #include <signal.h> #include <sys/types.h> #include <sys/param.h> #include <errno.h> #include <unistd.h> #include "meta.h" /* * Globals */ int verbose = FALSE; /* if TRUE, print extra info on terminal */ char *prog = NULL; /* our program name */ SERVER *servers = NULL; /* list of servers to check */ int server_count = 0; /* #of servers in list */ PORT *ports = NULL; /* list of ports to listen on */ int port_count = 0; /* #of ports in list */ EXCLUDE *excludes = NULL; /* list of clients to exclude */ int exclude_count = 0; int try_alt_port = TRUE; /* try port-1 first? */ int log_dns_names = TRUE; /* do DNS lookup on hostnames for log file? */ int save_ckp = TRUE; /* save checkpoint file? */ int wait_noconn = 30; /* wait 30 mins if connection failed */ int wait_empty = 15; /* wait 15 mins if server is empty */ int wait_open = 15; /* wait 15 mins if server is open */ int wait_queue = 15; /* wait 15 mins if server has a wait queue */ time_t uptime = 0; /* when we started */ time_t checktime = 0; /* when we started collecting data */ char info_file[MAXPATHLEN]; /* pathname of info file */ char faq_file[MAXPATHLEN]; /* pathmame of faq file */ char location[80]; /* city/state/country */ char admin[80]; /* e-mail addr of MSII maintainer */ FLAG flags[MAX_FLAG] = { { DF_HAD_TMODE, 'T', "HAD-TMODE" }, { DF_ALWAYS_DEAD, 'D', "ALWAYS-DEAD" }, { DF_RSA, 'R', "RSA-RESERVED" }, }; PORT_DEF port_defs[] = { { DI_OLD, "old" }, { DI_NEW, "new" }, { DI_WEB, "web" }, { DI_VERBOSE, "verbose" }, { DI_INFO, "info" }, }; #define MAX_PORT_DEF (sizeof(port_defs) / sizeof(PORT_DEF)) #define PIDFILE "meta.pid" /* * Private variables */ static FILE *logfp = NULL; /* log file */ static int do_log = TRUE; /* set from config file */ static int trun = 0; /* truncate log file on startup? */ /* * Configuration stuff */ typedef enum { STMT_BOOL, STMT_INT, STMT_STRING, STMT_SERVER, STMT_PORT, STMT_EXCLUDE } STMT_KIND; struct keyword_t { STMT_KIND kind; char *keyword; int *var; } keywords[] = { { STMT_INT, "wait_noconn", &wait_noconn }, { STMT_INT, "wait_empty", &wait_empty }, { STMT_INT, "wait_open", &wait_open }, { STMT_INT, "wait_queue", &wait_queue }, { STMT_BOOL, "do_logging", &do_log }, { STMT_BOOL, "log_dns_names", &log_dns_names }, { STMT_BOOL, "try_alt_port", &try_alt_port }, { STMT_BOOL, "save_ckp", &save_ckp }, { STMT_STRING, "info_file", (int *) info_file }, { STMT_STRING, "faq_file", (int *) faq_file }, { STMT_STRING, "location", (int *) location }, { STMT_STRING, "admin", (int *) admin }, { STMT_SERVER, "servers", NULL }, { STMT_PORT, "port", NULL }, { STMT_EXCLUDE, "exclude", NULL }, }; #define MAX_KEYWORDS (sizeof(keywords) / sizeof(struct keyword_t)) /* * print a message to the log * * (I'd use varargs, but vfprintf() doesn't exist everywhere, so why bother?) */ void log_msg(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) char *format; int arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7; { char buf[256]; if (!do_log) return; if (logfp == NULL) { fprintf(stderr, "WARNING: attempt to write message to closed log\n"); return; } /* date is in hex to keep it compact */ sprintf(buf, "%.8lx: ", time(0)); sprintf(buf+10, format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); if (*(buf + strlen(buf)-1) != '\n') strcat(buf, "\n"); /* since there should only be one metaserver process, we probably don't */ /* need to seek to the end every time (but it allows truncation...) */ fseek(logfp, (off_t) 0, 2); fprintf(logfp, buf); } /* add an entry to the "server" list */ static void add_server(type, host, iphost, port, players, comment) char *type, *host, *iphost, *port, *players, *comment; { int portnum = atoi(port); int playernum = atoi(players); int rsa; if (servers == NULL) { servers = (SERVER *) malloc(sizeof(SERVER)); } else { servers = (SERVER *) realloc(servers, sizeof(SERVER) * (server_count+1)); } if (servers == NULL) { fprintf(stderr, "ERROR: malloc/realloc failure (servers)\n"); exit(1); } servers[server_count].status = SS_INIT; servers[server_count].cstate = CS_CLOSED; if (!strncmp(host, "RSA", 3)) { rsa = 1; strcpy(servers[server_count].hostname, host+3); } else { rsa = 0; strcpy(servers[server_count].hostname, host); } strcpy(servers[server_count].ip_addr, iphost); if ((servers[server_count].addr = lookup(host, iphost)) == -1) return; servers[server_count].type[0] = *type; servers[server_count].port = portnum; servers[server_count].max_players = playernum; servers[server_count].count = 0; servers[server_count].last_update = (time_t) 0; servers[server_count].down_time = 0; /* set the next_update time to space the checks 40 seconds apart */ servers[server_count].next_update = (time_t) (time(0) + server_count*START_DIST); servers[server_count].flags = 0L; servers[server_count].display_flags = (DF_ALWAYS_DEAD) | (DF_RSA * rsa); if (comment == NULL) strcpy(servers[server_count].comment, ""); else strcpy(servers[server_count].comment, comment); servers[server_count].player_count = 0; servers[server_count].solicit = FALSE; server_count++; } /* add an entry to the "port" list */ static void add_port(port, type, description, extra) char *port, *type, *description, *extra; { DISPLAY kind; int portnum = atoi(port); int i; /* see if portnum is valid and port kind is known */ if (portnum < 1024) { fprintf(stderr, "WARNING: port %d (%s) reserved; stmt ignored\n", portnum, port); return; } for (i = 0; i < MAX_PORT_DEF; i++) { if (!strcmp(type, port_defs[i].port_name)) { kind = port_defs[i].port_kind; break; } } if (i == MAX_PORT_DEF) { fprintf(stderr, "WARNING: unknown port kind '%s'\n", type); return; } /* create a new entry, and add it to the list */ if (ports == NULL) { ports = (PORT *) malloc(sizeof(PORT)); } else { ports = (PORT *) realloc(ports, sizeof(PORT) * (port_count+1)); } if (ports == NULL) { fprintf(stderr, "ERROR: malloc/realloc failure (ports)\n"); exit(1); } ports[port_count].port = portnum; ports[port_count].kind = kind; strcpy(ports[port_count].desc, description); if (extra != NULL) strcpy(ports[port_count].extra, extra); else ports[port_count].extra[0] = '\0'; #ifdef DEBUG printf("port=%d, kind=%d, desc='%s', extra='%s'\n", ports[port_count].port, ports[port_count].kind, ports[port_count].desc, ports[port_count].extra); #endif port_count++; } /* add an entry to the "exclude" list */ static void add_exclude(host) char *host; { if (excludes == NULL) { excludes = (EXCLUDE *) malloc(sizeof(EXCLUDE)); } else { excludes = (EXCLUDE *) realloc(excludes, sizeof(EXCLUDE) * (exclude_count+1)); } if (excludes == NULL) { fprintf(stderr, "ERROR: malloc/realloc failure (excludes)\n"); exit(1); } if ((excludes[exclude_count].addr = lookup(host, host)) == -1) return; #ifdef DEBUG printf("exclude %s (0x%lx)\n", host, excludes[exclude_count].addr); #endif exclude_count++; } /* for some stupid reason my inet_ntoa() is crashing (bad shared libs??) */ char * my_ntoa(val) unsigned long val; { static char buf[16]; unsigned long foo; char *cp; int i; cp = buf; for (i = 3; i >= 0; i--) { /* sadly, sprintf() doesn't return #of chars on BSD systems */ foo = val >> (8*i); sprintf(cp, "%d.", foo & 0xff); cp += strlen(cp); } *(cp-1) = '\0'; /* trim the last '.' off */ return (buf); } /* print the list of excludes after the log file is open */ static void print_excludes() { int i; for (i = 0; i < exclude_count; i++) log_msg("exclude #%d is %s (0x%.8lx)", i, my_ntoa(excludes[i].addr), excludes[i].addr); } /* find the next token */ static char * next_token(str) char *str; { /* skip leading whitespace */ while (*str && (*str == ' ' || *str == '\t' || *str == '\n')) str++; if (*str) return (str); else return (NULL); } /* * read the configuration in (may be called from a signal handler) */ static void load_config() { FILE *fp; char buf[128], *cp, *cp2, *cp3, *cp4, *cp5, *cp6; int i, len, line; int serv_mode = FALSE; /* clear out any existing configuration stuff */ if (server_count) { free(servers); server_count = 0; } *info_file = *faq_file = *admin = *location = '\0'; if ((fp = fopen(CONFIGFILE, "r")) == NULL) { perror("ERROR: can't open config file"); exit(1); } line = 0; while (1) { fgets(buf, 128, fp); line++; if (feof(fp)) break; if (ferror(fp)) { perror("ERROR: error reading config file"); exit(1); } if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; /* skip comments and blank lines */ cp = next_token(buf); if (cp == NULL || *cp == '#') continue; /* once serv_mode is TRUE, we don't do anything but snag server names */ if (serv_mode) { cp = strtok(cp, " \t"); if ((cp2 = strtok(NULL, " \t")) == NULL) { /* Server name */ fprintf(stderr, "WARNING: missing server name (line %d); ignored\n", line); break; } if ((cp3 = strtok(NULL, " \t")) == NULL) { /* IP address */ fprintf(stderr, "WARNING: missing IP host (line %d); ignored\n", line); break; } if ((cp4 = strtok(NULL, " \t")) == NULL) { /* Port */ fprintf(stderr, "WARNING: missing port (line %d); ignored\n", line); break; } if ((cp5 = strtok(NULL, " \t")) == NULL) { /* Player # */ fprintf(stderr, "WARNING: missing player number (line %d); ignored\n", line); break; } cp6 = next_token(cp4+strlen(cp5)+1); if (verbose) printf("Line is: %s %s %s %s %s %s\n", cp, cp2, cp3, cp4, cp5, cp6); add_server(cp, cp2, cp3, cp4, cp5, cp6); continue; } /* find a token in "keywords" which matches */ for (i = 0; i < MAX_KEYWORDS; i++) { len = strlen(keywords[i].keyword); /* use table for speed? */ if (!strncmp(cp, keywords[i].keyword, len)) { cp2 = next_token(cp+len); if (cp2 != NULL && *cp2 == ':') { break; } } } if (i == MAX_KEYWORDS) { fprintf(stderr, "WARNING: unknown statement (line %d): %s\n", line, buf); continue; } /* parse the right-hand side with strtok (unless it's a string) */ if (keywords[i].kind == STMT_STRING || keywords[i].kind == STMT_SERVER){ cp = next_token(cp2+1); } else if ((cp = strtok(cp2+1, " \t")) == NULL) { fprintf(stderr, "WARNING: missing rhs (line %d); ignored\n", line); continue; } switch (keywords[i].kind) { case STMT_BOOL: /* boolean on/off */ if (!strcasecmp(cp, "on")) *(keywords[i].var) = 1; else if (!strcasecmp(cp, "off")) *(keywords[i].var) = 0; else fprintf(stderr, "WARNING: boolean expr must be 'on' or 'off' (line %d)\n", line); break; case STMT_INT: /* integer value */ *((int *) keywords[i].var) = atoi(cp); break; case STMT_STRING: /* string; dest should be allocated */ if (keywords[i].var == NULL) { fprintf(stderr, "WARNING: no storage\n"); } else { strcpy((char *) keywords[i].var, cp); } break; case STMT_PORT: /* "port" line */ if ((cp2 = strtok(NULL, " \t")) == NULL) { fprintf(stderr, "WARNING: port missing port kind; ignored\n"); break; } if ((cp3 = strtok(NULL, "\"\t")) == NULL) { fprintf(stderr, "WARNING: port missing description; ignored\n"); break; } cp4 = strtok(NULL, " \t"); add_port(cp, cp2, cp3, cp4); break; case STMT_EXCLUDE: /* "exclude" line */ add_exclude(cp); break; case STMT_SERVER: /* "server" line */ serv_mode = TRUE; break; default: /* "shouldn't happen" */ fprintf(stderr, "Internal error: bad keyword kind %d\n", keywords[i].kind); } } /* sanity check: make sure we have something to do! */ if (!server_count) { fprintf(stderr, "ERROR: no servers in list!\n"); exit(1); } if (!port_count) { fprintf(stderr, "ERROR: no ports to listen on!\n"); exit(1); } #ifdef DEBUG2 for (i = 0; i < server_count; i++) { printf("%d: %s\n", i, servers[i].hostname); } #endif } /* * open the log file and print a friendly message */ static void open_log() { time_t now; char *ostr; if (!do_log) return; /* prepare the log file */ if (!trun) ostr = "a"; /* just append */ else ostr = "w"; /* truncate */ if ((logfp = fopen(LOGFILE, ostr)) == NULL) { perror("ERROR: can't open log file for writing"); exit(1); } else { setvbuf(logfp, NULL, _IONBF, 0); /* no buffering */ log_msg("\n"); now = time(0); log_msg("** MetaServerII v%s started on %s", VERSION, ctime(&now)); } } /* * Save status to checkpoint file * * Just does a dump of server structs. Changing the struct makes existing * checkpoint files worthless. Puts the current time and the start time at * the head of the file. */ void save_checkpoint() { FILE *fp; time_t now; if (!save_ckp) { log_msg("checkpoint NOT saved"); return; } if ((fp = fopen(CHECKFILE, "w")) == NULL) { if (verbose) perror("fopen(checkfile)"); log_msg("unable to open checkpoint file (err=%d)", errno); return; } now = time(0); if (fwrite((char *)&now, sizeof(time_t), 1, fp) <= 0) { if (verbose) perror("fwrite(checkfile) time-now"); log_msg("unable to write to checkpoint file (t)(err=%d)", errno); fclose(fp); return; } if (fwrite((char *)&checktime, sizeof(time_t), 1, fp) <= 0) { if (verbose) perror("fwrite(checkfile) time-begin"); log_msg("unable to write to checkpoint file (t2)(err=%d)", errno); fclose(fp); return; } if (fwrite((char *)servers, sizeof(SERVER), server_count, fp) <= 0) { if (verbose) perror("fwrite(checkfile)"); log_msg("unable to write to checkpoint file (err=%d)", errno); fclose(fp); return; } fclose(fp); log_msg("checkpoint saved"); } /* * Restore status from checkpoint file * * The server list may have changed, so we need to match each item from the * checkpoint file with an item in the server list. This is not trapped * automatically. * * If the checkpoint file is corrupted, we can get pretty messed up. */ void restore_checkpoint() { SERVER *sp, srvbuf; FILE *fp; time_t filetime, now; int i, j; if ((fp = fopen(CHECKFILE, "r")) == NULL) { if (verbose) perror("fopen(checkfile)"); log_msg("unable to open checkpoint file (r)(err=%d)", errno); return; } if (fread((char *)&filetime, sizeof(time_t), 1, fp) <= 0) { if (verbose) perror("fread(checkfile) time"); log_msg("unable to read checkpoint file (t)(err=%d)", errno); fclose(fp); return; } if (uptime - filetime > MAX_CKP_AGE) { if (verbose) fprintf(stderr, "Warning: checkpoint file too old (ignored)\n"); log_msg("checkpoint file ignored (%d seconds old, max is %d)", uptime - filetime, MAX_CKP_AGE); goto dodel; } if (verbose) printf("Checkpoint file is %d seconds old\n", uptime - filetime); if (fread((char *)&checktime, sizeof(time_t), 1, fp) <= 0) { if (verbose) perror("fread(checkfile) time"); log_msg("unable to read checkpoint file (t)(err=%d)", errno); fclose(fp); return; } /* for each entry in the checkpoint file, find the matching SERVER */ /* (if config info has changed, ignore the checkpoint entry) */ while (1) { if (fread((char *)(&srvbuf), sizeof(SERVER), 1, fp) <= 0) break; #ifdef DEBUG printf("Got '%s'\n", srvbuf.hostname); #endif /* shouldn't compare against already-matched servers... */ for (i = 0, sp = servers; i < server_count; i++, sp++) { if (!strcmp(srvbuf.hostname, sp->hostname) && !strcmp(srvbuf.ip_addr, sp->ip_addr) && !strcmp(srvbuf.comment, sp->comment) && (srvbuf.type[0] == sp->type[0]) && srvbuf.port == sp->port) { memcpy(sp, &srvbuf, sizeof(SERVER)); } } } /* do some minor cleaning in case we interrupted it last time */ /* while we're here, bump up "next check" times for new servers */ now = time(0); for (i = j = 0, sp = servers; i < server_count; i++, sp++) { sp->pstate = PS_PORTM; sp->cstate = CS_CLOSED; if (sp->status == SS_WORKING) sp->status = SS_INIT; if (sp->status == SS_INIT) sp->next_update = (time_t) (now + (j++) * START_DIST); } log_msg("checkpoint restored"); /* delete the checkpoint file */ dodel: if (unlink(CHECKFILE) < 0) { if (verbose) perror("unlink(checkfile)"); log_msg("unable to unlink checkfile (err=%d)", errno); } } /* fatal signal received */ static int sig_death(sig, code, scp, addr) int sig, code; struct sigcontext *scp; char *addr; { if (verbose) printf("%s: killed by signal %d\n", prog, sig); log_msg("exit: killed by signal %d", sig); save_checkpoint(); #ifdef ABORT #ifndef PROF /* always exit cleanly if we're doing profiling */ log_msg("sig = %d, code = %d, scp = 0x%.8lx, addr = 0x%.8lx\n", sig, code, scp, addr); log_msg("pc = 0x%.8lx\n", scp->sc_pc); log_msg("Dumping core...\n"); abort(); #endif #endif /*ABORT*/ exit(2); } /* reconfigure signal receieved */ static int sig_reconfig(sig) int sig; { if (verbose) printf("%s: received reconfigure signal %d\n", prog, sig); /*log_msg("rereading configuration file (signal %d)", sig);*/ log_msg("ignoring config signal %d", sig); /*load_config();*/ } /* * initialize signal handling stuff */ static void init_signals() { SIGNAL(SIGINT, sig_death); SIGNAL(SIGQUIT, sig_death); SIGNAL(SIGTERM, sig_death); SIGNAL(SIGHUP, sig_reconfig); SIGNAL(SIGPIPE, SIG_IGN); /*SIGNAL(SIGSEGV, sig_crash);*/ SIGNAL(SIGALRM, wakeup); SIGNAL(SIGCHLD, sig_child); } /* * Process args */ int main(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind; int c; FILE *fp; int pid = getpid(); if ((fp = fopen(PIDFILE,"w"))==NULL) { perror("pid file"); exit(0); } fprintf(fp,"%d\n", pid); fclose(fp); /* parse args */ while ((c = getopt(argc, argv, "vtd")) != EOF) switch (c) { case 'v': verbose++; break; case 't': trun++; break; case 'd': BecomeDaemon(); break; case '?': default: fprintf(stderr, "Netrek MetaServerII v%s\n", VERSION); fprintf(stderr, "usage: metaserverII [-d] [-v] [-t]\n\n"); exit(2); } if ((prog = strrchr(argv[0], '/')) == NULL) prog = argv[0]; else prog++; /* read the configuration file */ load_config(); /* initialize internal data structures */ init_connections(); /* open the log and print a friendly message */ if (do_log) open_log(); /* init the signal handling stuff */ init_signals(); uptime = checktime = time(0); /* if there's a checkpoint file, load the old data */ restore_checkpoint(); /* print the list of excluded hosts, for reference */ print_excludes(); /* nothing has failed yet, so go wait for connections */ main_loop(); exit(0); /*NOTREACHED*/ } --- NEW FILE: disp_web.c --- /* * disp_web.c - output in fancy new format * * MetaServerII * Copyright (c) 1993 by Andy McFadden * * $Id: disp_web.c,v 1.1 2006/02/14 06:43:11 unbelver Exp $ * */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include "meta.h" #define SEMI_VERBOSE /* not quite fully verbose */ /* "when" should be the length of time in seconds */ static char * nice_time(when) time_t when; { static char buf[64]; if (when < 120) sprintf(buf, "%d seconds", when); else if (when/60 < 120) sprintf(buf, "%d minutes", when/60); else if (when/3600 < 48) sprintf(buf, "%d hours", when/3600); else sprintf(buf, "%d days", when/(3600*24)); return (buf); } int display_web(idx, port_idx) int idx, port_idx; { register int i; USER *up; SERVER *sp; time_t now; int srv, ago, *sorted; char flagbuf[8], buf[128]; char *cp; up = &users[idx]; if(up->buf) { free(up->buf); up->buf = NULL; } up->data_size = up->buf_size = up->pos = 0; now = time(0); /* print server header */ Uprintf(idx, "HTTP/1.0 200 OK\n"); Uprintf(idx, "Server: netrekmetarc/1.0\n"); Uprintf(idx, "MIME-version: 1.0\n"); Uprintf(idx, "Content-type: text/html\n\n"); /* print page */ Uprintf(idx, "\ <html><title>MetaServer II Server List</title>\n\ <body><center><h1>MetaServer II Server List</h1></center>\n"); Uprintf(idx, "<center><table border=2 cellpadding=6><tr>\ <td><h4>Server Host</h4></td>\ <td><h4>Port</h4></td>\ <td><h4>Minutes Ago</h4></td>\ <td><h4>Status</h4></td>\ <td><h4>Flags</h4></td>\ </tr>\n"); /* print player info */ sorted = sort_servers(); for (srv = 0; srv < server_count; srv++) { /* (need to sort these?) */ sp = &servers[sorted[srv]]; if (sp->status == SS_WORKING) sp = &prev_info; Uprintf(idx, "\n<tr><td>%s</td><td>%d</td>\n", sp->hostname, sp->port); strcpy(flagbuf, " "); for (i = 0; i < 6; i++) if (sp->display_flags & flags[i].bit) flagbuf[i] = flags[i].chr; flagbuf[6] = sp->type[0]; /* hack in the type field */ ago = (now - sp->last_update) / 60; switch (sp->status) { case SS_INIT: Uprintf(idx, "<td colspan=3>(no data yet)</td>\n"); break; case SS_WORKING: Uprintf(idx, "<td colspan=3>(scanning server now)</td>\n"); break; case SS_NOCONN: switch (sp->why_dead) { case WD_CONN: Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* No connection", flagbuf); break; case WD_READ: Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Bad read", flagbuf); break; case WD_GARBAGE: Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Garbaged read", flagbuf); break; case WD_TIMEOUT: Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Timed out", flagbuf); break; case WD_FLOODING: Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Flooding", flagbuf); break; default: Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Not responding", flagbuf); break; } break; case SS_EMPTY: Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "Nobody playing", flagbuf); break; case SS_OPEN: sprintf(buf, "OPEN: %d player%s", sp->player_count, (sp->player_count != 1) ? "s" : ""); Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, buf, flagbuf); break; case SS_QUEUE: sprintf(buf, "Wait queue: %d", sp->queue_size); Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, buf, flagbuf); break; default: /* huh? */ fprintf(stderr, "Internal ERROR: strange state in disp_new\n"); break; } Uprintf(idx, "</tr>\n"); } /* print footer */ Uprintf(idx, "</table></center>\n"); Uprintf(idx, "<p><i>(This is MetaServerII v%s on port %d, \n", VERSION, ports[port_idx].port); cp = ctime(&now); cp[strlen(cp)-1] = '\0'; /* playing with fire? */ Uprintf(idx, "date in %s is %s, data spans %s, \n", location, cp, nice_time(now-checktime)); Uprintf(idx, "send administrative requests to <a href=mailto:%s>%s</a>, ", admin, admin); Uprintf(idx, "retry periods in minutes are "); Uprintf(idx, "down:%d empty:%d open:%d queue:%d)</i><p>\n\n", wait_noconn, wait_empty, wait_open, wait_queue); Uprintf(idx, "<p>Link to <a href=\"http://metaserver.netrek.org:1080/\">US metaserver</a><p>\n"); Uprintf(idx, "<p>Link to <a href=\"http://metaserver.eu.netrek.org:1080/\">European metaserver</a></body></html>\n"); return(0); } --- NEW FILE: metaII_info --- Welcome to MetaServerII's News & Information port! METASERVER-II SERVICES: Port 3520 has the "look & feel" of the original METASERVER. It's fully compatible with programs like XNetrekM. Port 3521 is a new and improved format. It incorporates some of the suggestions seen on rec.games.netrek. Port 3522 provides verbose output, giving player lists (when available) as well as all of the information available from the above two ports. Port 3523 is what you are reading now. Port 3524 is the server list posted to rec.games.netrek once per month. It has a complete list of known public servers, with some detailed info about each. Port 3525 is the MetaServerII config file. Server admins may want to look at this to see if/how their server is listed. Port 3530 is the list of RSA public keys. This is only useful for server administrators; you don't need this just to play. You can save a copy of the info with: telnet metaserver.netrek.org <port> > file (replace <port> with one of the numbers above.) EXPLANATION OF "FLAGS": T Means that at some point in the lifetime of the MetaServerII process there were >= 8 players on this server ("T-mode"). D Means that this server hasn't responded since MetaServerII started watching ("always Dead"). R Means that this server supports (and may REQUIRE) RSA validation. The server types are as follows. These are broad categories, indicating a general "feel" of play. B Bronco - standard scam/bronco sources. P Paradise - paradise-style server. S Sturgeon - upgrades and weird weapons, planets, etc. C Chaos - lots of really whacked out mods. H Hockey - works like hockey. F Dogfighting - dogfighting server. I CAN'T SEEM TO GET THE ENTIRE LISTING Try sending it to a file: ("telnet metaserver.netrek.org 3521 > /tmp/mlist; cat /tmp/mlist"). If that doesn't work, I have a short (70 line) program which WILL work. Send mail and I'll forward it. MASS RETRIEVALS: DO NOT run scripts or MS-II clients that automatically read in every port every time. If everybody ran one of these the extra load would be difficult to bear. Ask for all the information you want, but don't load up the network with requests for information you're not interested in (the clients usually get both ports 3520 and 3521, which contains essentially identical information, and the faq from port 3524, which most people don't need to read more than once or twice). Repeat offenders will be added to the "exclude" list, which blocks a host from all access. Currently ALL of richsunN.gatech.edu is blocked for exactly this reason. WHERE CAN I GET MORE INFORMATION? Try rec.games.netrek. Use "rn", "readnews", "vnews", "nn", "notes", or whatever else you have to read Usenet news. If you can't quite figure it out, ask someone on your system. There's a Frequently Asked Questions list for rec.games.netrek. I would include it here except that you really ought to be reading the newsgroup anyway.