ChangeLog update
[tetrinet.git] / server.c
blob82c4f64eeff97bae7fafbc91b3a75459890f3802
1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2  * This program is public domain.
3  *
4  * Tetrinet server code
5  */
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <sys/types.h>
14 #include <netinet/in.h>
15 /* Due to glibc brokenness, we can't blindly include this.  Yet another
16  * reason to not use glibc. */
17 /* #include <netinet/protocols.h> */
18 #include <signal.h>
19 #include <sys/socket.h>
20 #include <sys/time.h>
21 #include <unistd.h>
22 #include "tetrinet.h"
23 #include "tetris.h"
24 #include "server.h"
25 #include "sockets.h"
27 /*************************************************************************/
29 static int linuxmode = 0;  /* 1: don't try to be compatible with Windows */
30 static int ipv6_only = 0;  /* 1: only use IPv6 (when available) */
32 static int quit = 0;
34 static int listen_sock = -1;
35 #ifdef HAVE_IPV6
36 static int listen_sock6 = -1;
37 #endif
38 static int player_socks[6] = {-1,-1,-1,-1,-1,-1};
39 static unsigned char player_ips[6][4];
40 static int player_modes[6];
42 /* Which players have already lost in the current game? */
43 static int player_lost[6];
45 /* We re-use a lot of variables from the main code */
47 /*************************************************************************/
48 /*************************************************************************/
50 /* Convert a 2-byte hex value to an integer. */
52 int xtoi(const char *buf)
54     int val;
56     if (buf[0] <= '9')
57         val = (buf[0] - '0') << 4;
58     else
59         val = (toupper(buf[0]) - 'A' + 10) << 4;
60     if (buf[1] <= '9')
61         val |= buf[1] - '0';
62     else
63         val |= toupper(buf[1]) - 'A' + 10;
64     return val;
67 /*************************************************************************/
69 /* Return a string containing the winlist in a format suitable for sending
70  * to clients.
71  */
73 static char *winlist_str()
75     static char buf[1024];
76     char *s;
77     int i;
79     s = buf;
80     for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
81         s += snprintf(s, sizeof(buf)-(s-buf),
82                         linuxmode ? " %c%s;%d;%d" : " %c%s;%d",
83                         winlist[i].team ? 't' : 'p',
84                         winlist[i].name, winlist[i].points, winlist[i].games);
85     }
86     return buf;
89 /*************************************************************************/
90 /*************************************************************************/
92 /* Read the configuration file. */
94 void read_config(void)
96     char buf[1024], *s, *t;
97     FILE *f;
98     int i;
100     s = getenv("HOME");
101     if (!s)
102         s = "/etc";
103     snprintf(buf, sizeof(buf), "%s/.tetrinet", s);
104     if (!(f = fopen(buf, "r")))
105         return;
106     while (fgets(buf, sizeof(buf), f)) {
107         s = strtok(buf, " ");
108         if (!s) {
109             continue;
110         } else if (strcmp(s, "linuxmode") == 0) {
111             if ((s = strtok(NULL, " ")))
112                 linuxmode = atoi(s);
113         } else if (strcmp(s, "ipv6_only") == 0) {
114             if ((s = strtok(NULL, " ")))
115                 ipv6_only = atoi(s);
116         } else if (strcmp(s, "averagelevels") == 0) {
117             if ((s = strtok(NULL, " ")))
118                 level_average = atoi(s);
119         } else if (strcmp(s, "classic") == 0) {
120             if ((s = strtok(NULL, " ")))
121                 old_mode = atoi(s);
122         } else if (strcmp(s, "initiallevel") == 0) {
123             if ((s = strtok(NULL, " ")))
124                 initial_level = atoi(s);
125         } else if (strcmp(s, "levelinc") == 0) {
126             if ((s = strtok(NULL, " ")))
127                 level_inc = atoi(s);
128         } else if (strcmp(s, "linesperlevel") == 0) {
129             if ((s = strtok(NULL, " ")))
130                 lines_per_level = atoi(s);
131         } else if (strcmp(s, "pieces") == 0) {
132             i = 0;
133             while (i < 7 && (s = strtok(NULL, " ")))
134                 piecefreq[i++] = atoi(s);
135         } else if (strcmp(s, "specialcapacity") == 0) {
136             if ((s = strtok(NULL, " ")))
137                 special_capacity = atoi(s);
138         } else if (strcmp(s, "specialcount") == 0) {
139             if ((s = strtok(NULL, " ")))
140                 special_count = atoi(s);
141         } else if (strcmp(s, "speciallines") == 0) {
142             if ((s = strtok(NULL, " ")))
143                 special_lines = atoi(s);
144         } else if (strcmp(s, "specials") == 0) {
145             i = 0;
146             while (i < 9 && (s = strtok(NULL, " ")))
147                 specialfreq[i++] = atoi(s);
148         } else if (strcmp(s, "winlist") == 0) {
149             i = 0;
150             while (i < MAXWINLIST && (s = strtok(NULL, " "))) {
151                 t = strchr(s, ';');
152                 if (!t)
153                     break;
154                 *t++ = 0;
155                 strncpy(winlist[i].name, s, sizeof(winlist[i].name)-1);
156                 winlist[i].name[sizeof(winlist[i].name)-1] = 0;
157                 s = t;
158                 t = strchr(s, ';');
159                 if (!t) {
160                     *winlist[i].name = 0;
161                     break;
162                 }
163                 winlist[i].team = atoi(s);
164                 s = t+1;
165                 t = strchr(s, ';');
166                 if (!t) {
167                     *winlist[i].name = 0;
168                     break;
169                 }
170                 winlist[i].points = atoi(s);
171                 winlist[i].games = atoi(t+1);
172                 i++;
173             }
174         }
175     }
176     fclose(f);
179 /*************************************************************************/
181 /* Re-write the configuration file. */
183 void write_config(void)
185     char buf[1024], *s;
186     FILE *f;
187     int i;
189     s = getenv("HOME");
190     if (!s)
191         s = "/etc";
192     snprintf(buf, sizeof(buf), "%s/.tetrinet", s);
193     if (!(f = fopen(buf, "w")))
194         return;
196     fprintf(f, "winlist");
197     for (i = 0; i < MAXSAVEWINLIST && *winlist[i].name; i++) {
198         fprintf(f, " %s;%d;%d;%d", winlist[i].name, winlist[i].team,
199                                    winlist[i].points, winlist[i].games);
200     }
201     fputc('\n', f);
203     fprintf(f, "classic %d\n", old_mode);
205     fprintf(f, "initiallevel %d\n", initial_level);
206     fprintf(f, "linesperlevel %d\n", lines_per_level);
207     fprintf(f, "levelinc %d\n", level_inc);
208     fprintf(f, "averagelevels %d\n", level_average);
210     fprintf(f, "speciallines %d\n", special_lines);
211     fprintf(f, "specialcount %d\n", special_count);
212     fprintf(f, "specialcapacity %d\n", special_capacity);
214     fprintf(f, "pieces");
215     for (i = 0; i < 7; i++)
216         fprintf(f, " %d", piecefreq[i]);
217     fputc('\n', f);
219     fprintf(f, "specials");
220     for (i = 0; i < 9; i++)
221         fprintf(f, " %d", specialfreq[i]);
222     fputc('\n', f);
224     fprintf(f, "linuxmode %d\n", linuxmode);
225     fprintf(f, "ipv6_only %d\n", ipv6_only);
227     fclose(f);
230 /*************************************************************************/
231 /*************************************************************************/
233 /* Send a message to a single player. */
235 static void send_to(int player, const char *format, ...)
237     va_list args;
238     char buf[1024];
240     va_start(args, format);
241     vsnprintf(buf, sizeof(buf), format, args);
242     if (player_socks[player-1] >= 0)
243         sockprintf(player_socks[player-1], "%s", buf);
246 /*************************************************************************/
248 /* Send a message to all players. */
250 static void send_to_all(const char *format, ...)
252     va_list args;
253     char buf[1024];
254     int i;
256     va_start(args, format);
257     vsnprintf(buf, sizeof(buf), format, args);
258     for (i = 0; i < 6; i++) {
259         if (player_socks[i] >= 0)
260             sockprintf(player_socks[i], "%s", buf);
261     }
264 /*************************************************************************/
266 /* Send a message to all players but the given one. */
268 static void send_to_all_but(int player, const char *format, ...)
270     va_list args;
271     char buf[1024];
272     int i;
274     va_start(args, format);
275     vsnprintf(buf, sizeof(buf), format, args);
276     for (i = 0; i < 6; i++) {
277         if (i+1 != player && player_socks[i] >= 0)
278             sockprintf(player_socks[i], "%s", buf);
279     }
282 /*************************************************************************/
284 /* Send a message to all players but those on the same team as the given
285  * player.
286  */
288 static void send_to_all_but_team(int player, const char *format, ...)
290     va_list args;
291     char buf[1024];
292     int i;
293     char *team = teams[player-1];
295     va_start(args, format);
296     vsnprintf(buf, sizeof(buf), format, args);
297     for (i = 0; i < 6; i++) {
298         if (i+1 != player && player_socks[i] >= 0 &&
299                         (!team || !teams[i] || strcmp(teams[i], team) != 0))
300             sockprintf(player_socks[i], "%s", buf);
301     }
304 /*************************************************************************/
305 /*************************************************************************/
307 /* Add points to a given player's [team's] winlist entry, or make a new one
308  * if they rank.
309  */
311 static void add_points(int player, int points)
313     int i;
315     if (!players[player-1])
316         return;
317     for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
318         if (!winlist[i].team && !teams[player-1]
319          && strcmp(winlist[i].name, players[player-1]) == 0)
320             break;
321         if (winlist[i].team && teams[player-1]
322          && strcmp(winlist[i].name, teams[player-1]) == 0)
323             break;
324     }
325     if (i == MAXWINLIST) {
326         for (i = 0; i < MAXWINLIST && winlist[i].points >= points; i++)
327             ;
328     }
329     if (i == MAXWINLIST)
330         return;
331     if (!*winlist[i].name) {
332         if (teams[player-1]) {
333             strncpy(winlist[i].name, teams[player-1], sizeof(winlist[i].name)-1);
334             winlist[i].name[sizeof(winlist[i].name)-1] = 0;
335             winlist[i].team = 1;
336         } else {
337             strncpy(winlist[i].name, players[player-1], sizeof(winlist[i].name)-1);
338             winlist[i].name[sizeof(winlist[i].name)-1] = 0;
339             winlist[i].team = 0;
340         }
341     }
342     winlist[i].points += points;
345 /*************************************************************************/
347 /* Add a game to a given player's [team's] winlist entry. */
349 static void add_game(int player)
351     int i;
353     if (!players[player-1])
354         return;
355     for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
356         if (!winlist[i].team && !teams[player-1]
357          && strcmp(winlist[i].name, players[player-1]) == 0)
358             break;
359         if (winlist[i].team && teams[player-1]
360          && strcmp(winlist[i].name, teams[player-1]) == 0)
361             break;
362     }
363     if (i == MAXWINLIST || !*winlist[i].name)
364         return;
365     winlist[i].games++;
368 /*************************************************************************/
370 /* Sort the winlist. */
372 static void sort_winlist()
374     int i, j, best, bestindex;
376     for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
377         best = winlist[i].points;
378         bestindex = i;
379         for (j = i+1; j < MAXWINLIST && *winlist[j].name; j++) {
380             if (winlist[j].points > best) {
381                 best = winlist[j].points;
382                 bestindex = j;
383             }
384         }
385         if (bestindex != i) {
386             WinInfo tmp;
387             memcpy(&tmp, &winlist[i], sizeof(WinInfo));
388             memcpy(&winlist[i], &winlist[bestindex], sizeof(WinInfo));
389             memcpy(&winlist[bestindex], &tmp, sizeof(WinInfo));
390         }
391     }
394 /*************************************************************************/
396 /* Take care of a player losing (which may end the game). */
398 static void player_loses(int player)
400     int i, j, order, end = 1, winner = -1, second = -1, third = -1;
402     if (player < 1 || player > 6 || player_socks[player-1] < 0)
403         return;
404     order = 0;
405     for (i = 1; i <= 6; i++) {
406         if (player_lost[i-1] > order)
407             order = player_lost[i-1];
408     }
409     player_lost[player-1] = order+1;
410     for (i = 1; i <= 6; i++) {
411         if (player_socks[i-1] >= 0 && !player_lost[i-1]) {
412             if (winner < 0) {
413                 winner = i;
414             } else if (!teams[winner-1] || !teams[i-1]
415                         || strcasecmp(teams[winner-1],teams[i-1]) != 0) {
416                 end = 0;
417                 break;
418             }
419         }
420     }
421     if (end) {
422         send_to_all("endgame");
423         playing_game = 0;
424         /* Catch the case where no players are left (1-player game) */
425         if (winner > 0) {
426             send_to_all("playerwon %d", winner);
427             add_points(winner, 3);
428             order = 0;
429             for (i = 1; i <= 6; i++) {
430                 if (player_lost[i-1] > order
431                         && (!teams[winner-1] || !teams[i-1]
432                             || strcasecmp(teams[winner-1],teams[i-1]) != 0)) {
433                     order = player_lost[i-1];
434                     second = i;
435                 }
436             }
437             if (order) {
438                 add_points(second, 2);
439                 player_lost[second-1] = 0;
440             }
441             order = 0;
442             for (i = 1; i <= 6; i++) {
443                 if (player_lost[i-1] > order
444                         && (!teams[winner-1] || !teams[i-1]
445                             || strcasecmp(teams[winner-1],teams[i-1]) != 0)
446                         && (!teams[second-1] || !teams[i-1]
447                             || strcasecmp(teams[second-1],teams[i-1]) != 0)) {
448                     order = player_lost[i-1];
449                     third = i;
450                 }
451             }
452             if (order)
453                 add_points(third, 1);
454             for (i = 1; i <= 6; i++) {
455                 if (teams[i-1]) {
456                     for (j = 1; j < i; j++) {
457                         if (teams[j-1] && strcasecmp(teams[i-1],teams[j-1])==0)
458                             break;
459                     }
460                     if (j < i)
461                         continue;
462                 }
463                 if (player_socks[i-1] >= 0)
464                     add_game(i);
465             }
466         }
467         sort_winlist();
468         write_config();
469         send_to_all("winlist %s", winlist_str());
470     }
471     /* One more possibility: the only player playing left the game, which
472      * means there are now no players left. */
473     if (!players[0] && !players[1] && !players[2] && !players[3]
474                     && !players[4] && !players[5])
475         playing_game = 0;
478 /*************************************************************************/
479 /*************************************************************************/
481 /* Parse a line from a client.  Destroys the buffer it's given as a side
482  * effect.  Return 0 if the command is unknown (or bad syntax), else 1.
483  */
485 static int server_parse(int player, char *buf)
487     char *cmd, *s, *t;
488     int i, tetrifast = 0;
490     cmd = strtok(buf, " ");
492     if (!cmd) {
493         return 1;
495     } else if (strcmp(cmd, "tetrisstart") == 0) {
496 newplayer:
497         s = strtok(NULL, " ");
498         t = strtok(NULL, " ");
499         if (!t)
500             return 0;
501         for (i = 1; i <= 6; i++) {
502             if (players[i-1] && strcasecmp(s, players[i-1]) == 0) {
503                 send_to(player, "noconnecting Nickname already exists on server!");
504                 return 0;
505             }
506         }
507         players[player-1] = strdup(s);
508         if (teams[player-1])
509             free(teams[player-1]);
510         teams[player-1] = NULL;
511         player_modes[player-1] = tetrifast;
512         send_to(player, "%s %d", tetrifast ? ")#)(!@(*3" : "playernum", player);
513         send_to(player, "winlist %s", winlist_str());
514         for (i = 1; i <= 6; i++) {
515             if (i != player && players[i-1]) {
516                 send_to(player, "playerjoin %d %s", i, players[i-1]);
517                 send_to(player, "team %d %s", i, teams[i-1] ? teams[i-1] : "");
518             }
519         }
520         if (playing_game) {
521             send_to(player, "ingame");
522             player_lost[player-1] = 1;
523         }
524         send_to_all_but(player, "playerjoin %d %s", player, players[player-1]);
526     } else if (strcmp(cmd, "tetrifaster") == 0) {
527         tetrifast = 1;
528         goto newplayer;
530     } else if (strcmp(cmd, "team") == 0) {
531         s = strtok(NULL, " ");
532         t = strtok(NULL, "");
533         if (!s || atoi(s) != player)
534             return 0;
535         if (teams[player])
536             free(teams[player]);
537         if (t)
538             teams[player] = strdup(t);
539         else
540             teams[player] = NULL;
541         send_to_all_but(player, "team %d %s", player, t ? t : "");
543     } else if (strcmp(cmd, "pline") == 0) {
544         s = strtok(NULL, " ");
545         t = strtok(NULL, "");
546         if (!s || atoi(s) != player)
547             return 0;
548         if (!t)
549             t = "";
550         send_to_all_but(player, "pline %d %s", player, t);
552     } else if (strcmp(cmd, "plineact") == 0) {
553         s = strtok(NULL, " ");
554         t = strtok(NULL, "");
555         if (!s || atoi(s) != player)
556             return 0;
557         if (!t)
558             t = "";
559         send_to_all_but(player, "plineact %d %s", player, t);
561     } else if (strcmp(cmd, "startgame") == 0) {
562         int total;
563         char piecebuf[101], specialbuf[101];
565         for (i = 1; i < player; i++) {
566             if (player_socks[i-1] >= 0)
567                 return 1;
568         }
569         s = strtok(NULL, " ");
570         t = strtok(NULL, " ");
571         if (!s)
572             return 1;
573         i = atoi(s);
574         if ((i && playing_game) || (!i && !playing_game))
575             return 1;
576         if (!i) {  /* end game */
577             send_to_all("endgame");
578             playing_game = 0;
579             return 1;
580         }
581         total = 0;
582         for (i = 0; i < 7; i++) {
583             if (piecefreq[i])
584                 memset(piecebuf+total, '1'+i, piecefreq[i]);
585             total += piecefreq[i];
586         }
587         piecebuf[100] = 0;
588         if (total != 100) {
589             send_to_all("plineact 0 cannot start game: Piece frequencies do not total 100 percent!");
590             return 1;
591         }
592         total = 0;
593         for (i = 0; i < 9; i++) {
594             if (specialfreq[i])
595                 memset(specialbuf+total, '1'+i, specialfreq[i]);
596             total += specialfreq[i];
597         }
598         specialbuf[100] = 0;
599         if (total != 100) {
600             send_to_all("plineact 0 cannot start game: Special frequencies do not total 100 percent!");
601             return 1;
602         }
603         playing_game = 1;
604         game_paused = 0;
605         for (i = 1; i <= 6; i++) {
606             if (player_socks[i-1] < 0)
607                 continue;
608             /* XXX First parameter is stack height */
609             send_to(i, "%s %d %d %d %d %d %d %d %s %s %d %d",
610                         player_modes[i-1] ? "*******" : "newgame",
611                         0, initial_level, lines_per_level, level_inc,
612                         special_lines, special_count, special_capacity,
613                         piecebuf, specialbuf, level_average, old_mode);
614         }
615         memset(player_lost, 0, sizeof(player_lost));
617     } else if (strcmp(cmd, "pause") == 0) {
618         if (!playing_game)
619             return 1;
620         s = strtok(NULL, " ");
621         if (!s)
622             return 1;
623         i = atoi(s);
624         if (i)
625             i = 1;      /* to make sure it's not anything else */
626         if ((i && game_paused) || (!i && !game_paused))
627             return 1;
628         game_paused = i;
629         send_to_all("pause %d", i);
631     } else if (strcmp(cmd, "playerlost") == 0) {
632         if (!(s = strtok(NULL, " ")) || atoi(s) != player)
633             return 1;
634         player_loses(player);
636     } else if (strcmp(cmd, "f") == 0) {   /* field */
637         if (!(s = strtok(NULL, " ")) || atoi(s) != player)
638             return 1;
639         if (!(s = strtok(NULL, "")))
640             s = "";
641         send_to_all_but(player, "f %d %s", player, s);
643     } else if (strcmp(cmd, "lvl") == 0) {
644         if (!(s = strtok(NULL, " ")) || atoi(s) != player)
645             return 1;
646         if (!(s = strtok(NULL, " ")))
647             return 1;
648         levels[player] = atoi(s);
649         send_to_all_but(player, "lvl %d %d", player, levels[player]);
651     } else if (strcmp(cmd, "sb") == 0) {
652         int from, to;
653         char *type;
655         if (!(s = strtok(NULL, " ")))
656             return 1;
657         to = atoi(s);
658         if (!(type = strtok(NULL, " ")))
659             return 1;
660         if (!(s = strtok(NULL, " ")))
661             return 1;
662         from = atoi(s);
663         if (from != player)
664             return 1;
665         if (to < 0 || to > 6 || player_socks[to-1] < 0 || player_lost[to-1])
666             return 1;
667         if (to == 0)
668             send_to_all_but_team(player, "sb %d %s %d", to, type, from);
669         else
670             send_to_all_but(player, "sb %d %s %d", to, type, from);
672     } else if (strcmp(cmd, "gmsg") == 0) {
673         if (!(s = strtok(NULL, "")))
674             return 1;
675         send_to_all("gmsg %s", s);
677     } else {  /* unrecognized command */
678         return 0;
680     }
682     return 1;
685 /*************************************************************************/
686 /*************************************************************************/
688 static void sigcatcher(int sig)
690     if (sig == SIGHUP) {
691         read_config();
692         signal(SIGHUP, sigcatcher);
693         send_to_all("winlist %s", winlist_str());
694     } else if (sig == SIGTERM || sig == SIGINT) {
695         quit = 1;
696         signal(sig, SIG_IGN);
697     }
700 /*************************************************************************/
702 /* Returns 0 on success, desired program exit code on failure */
704 static int init()
706     struct sockaddr_in sin;
707 #ifdef HAVE_IPV6
708     struct sockaddr_in6 sin6;
709 #endif
710     int i;
712     /* Set up some sensible defaults */
713     *winlist[0].name = 0;
714     old_mode = 1;
715     initial_level = 1;
716     lines_per_level = 2;
717     level_inc = 1;
718     level_average = 1;
719     special_lines = 1;
720     special_count = 1;
721     special_capacity = 18;
722     piecefreq[0] = 14;
723     piecefreq[1] = 14;
724     piecefreq[2] = 15;
725     piecefreq[3] = 14;
726     piecefreq[4] = 14;
727     piecefreq[5] = 14;
728     piecefreq[6] = 15;
729     specialfreq[0] = 18;
730     specialfreq[1] = 18;
731     specialfreq[2] = 3;
732     specialfreq[3] = 12;
733     specialfreq[4] = 0;
734     specialfreq[5] = 16;
735     specialfreq[6] = 3;
736     specialfreq[7] = 12;
737     specialfreq[8] = 18;
739     /* (Try to) read the config file */
740     read_config();
742     /* Catch some signals */
743     signal(SIGHUP, sigcatcher);
744     signal(SIGINT, sigcatcher);
745     signal(SIGTERM, sigcatcher);
747     /* Set up a listen socket */
748     if (!ipv6_only)
749         listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
750     if (listen_sock >= 0){
751         i = 1;
752         if (setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==0) {
753             memset(&sin, 0, sizeof(sin));
754             sin.sin_family = AF_INET;
755             sin.sin_port = htons(31457);
756             if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) == 0) {
757                 if (listen(listen_sock, 5) == 0) {
758                     goto ipv4_success;
759                 }
760             }
761         }
762         i = errno;
763         close(listen_sock);
764         errno = i;
765         listen_sock = -1;
766     }
767   ipv4_success:
769 #ifdef HAVE_IPV6
770     /* Set up an IPv6 listen socket if possible */
771     listen_sock6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
772     if (listen_sock6 >= 0) {
773         i = 1;
774         if (setsockopt(listen_sock6,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==0) {
775             memset(&sin6, 0, sizeof(sin6));
776             sin6.sin6_family = AF_INET6;
777             sin6.sin6_port = htons(31457);
778             if (bind(listen_sock6,(struct sockaddr *)&sin6,sizeof(sin6))==0) {
779                 if (listen(listen_sock6, 5) == 0) {
780                     goto ipv6_success;
781                 }
782             }
783         }
784         i = errno;
785         close(listen_sock6);
786         errno = i;
787         listen_sock6 = -1;
788     }
789   ipv6_success:
790 #else  /* !HAVE_IPV6 */
791     if (ipv6_only) {
792         fprintf(stderr,"ipv6_only specified but IPv6 support not available\n");
793         return 1;
794     }
795 #endif  /* HAVE_IPV6 */
797     if (listen_sock < 0
798 #ifdef HAVE_IPV6
799      && listen_sock6 < 0
800 #endif
801     ) {
802         return 1;
803     }
805     return 0;
808 /*************************************************************************/
810 static void
811 decrypt_message(char *buf, char *newbuf, char *iphashbuf)
813     int j, c, l = strlen(iphashbuf);
815     c = xtoi(buf);
816     for (j = 2; buf[j] && buf[j+1]; j += 2) {
817         int temp, d;
819         temp = d = xtoi(buf+j);
820         d ^= iphashbuf[((j/2)-1) % l];
821         d += 255 - c;
822         d %= 255;
823         newbuf[j/2-1] = d;
824         c = temp;
825     }
826     newbuf[j/2-1] = 0;
829 static void check_sockets()
831     fd_set fds;
832     int i, fd, maxfd;
834     FD_ZERO(&fds);
835     if (listen_sock >= 0)
836         FD_SET(listen_sock, &fds);
837     maxfd = listen_sock;
838 #ifdef HAVE_IPV6
839     if (listen_sock6 >= 0)
840         FD_SET(listen_sock6, &fds);
841     if (listen_sock6 > maxfd)
842         maxfd = listen_sock6;
843 #endif
844     for (i = 0; i < 6; i++) {
845         if (player_socks[i] != -1) {
846             if (player_socks[i] < 0)
847                 fd = (~player_socks[i]) - 1;
848             else
849                 fd = player_socks[i];
850             FD_SET(fd, &fds);
851             if (fd > maxfd)
852                 maxfd = fd;
853         }
854     }
856     if (select(maxfd+1, &fds, NULL, NULL, NULL) <= 0)
857         return;
859     if (listen_sock >= 0 && FD_ISSET(listen_sock, &fds)) {
860         struct sockaddr_in sin;
861         int len = sizeof(sin);
862         fd = accept(listen_sock, (struct sockaddr *)&sin, &len);
863         if (fd >= 0) {
864             for (i = 0; i < 6 && player_socks[i] != -1; i++)
865                 ;
866             if (i == 6) {
867                 sockprintf(fd, "noconnecting Too many players on server!");
868                 close(fd);
869             } else {
870                 player_socks[i] = ~(fd+1);
871                 memcpy(player_ips[i], &sin.sin_addr, 4);
872             }
873         }
874     } /* if (FD_ISSET(listen_sock)) */
876 #ifdef HAVE_IPV6
877     if (listen_sock6 >= 0 && FD_ISSET(listen_sock6, &fds)) {
878         struct sockaddr_in6 sin6;
879         int len = sizeof(sin6);
880         fd = accept(listen_sock6, (struct sockaddr *)&sin6, &len);
881         if (fd >= 0) {
882             for (i = 0; i < 6 && player_socks[i] != -1; i++)
883                 ;
884             if (i == 6) {
885                 sockprintf(fd, "noconnecting Too many players on server!");
886                 close(fd);
887             } else {
888                 player_socks[i] = ~(fd+1);
889                 memcpy(player_ips[i], (char *)(&sin6.sin6_addr)+12, 4);
890             }
891         }
892     } /* if (FD_ISSET(listen_sock6)) */
893 #endif
895     for (i = 0; i < 6; i++) {
896         char buf[1024];
898         if (player_socks[i] == -1)
899             continue;
900         if (player_socks[i] < 0)
901             fd = (~player_socks[i]) - 1;
902         else
903             fd = player_socks[i];
904         if (!FD_ISSET(fd, &fds))
905             continue;
906         sgets(buf, sizeof(buf), fd);
908         if (player_socks[i] < 0) {
909             /* Our extension: the client can give up on the meaningless
910              * encryption completely. */
911             if (strncmp(buf,"tetrisstart ",12) != 0) {
912                 /* Messy decoding stuff */
913                 char iphashbuf[16], newbuf[1024];
914                 unsigned char *ip;
915 #ifndef NO_BRUTE_FORCE_DECRYPTION
916                 int hashval;
917 #endif
919                 if (strlen(buf) < 2*13) {  /* "tetrisstart " + initial byte */
920                     close(fd);
921                     player_socks[i] = -1;
922                     continue;
923                 }
925                 ip = player_ips[i];
926                 sprintf(iphashbuf, "%d",
927                         ip[0]*54 + ip[1]*41 + ip[2]*29 + ip[3]*17);
928                 decrypt_message(buf, newbuf, iphashbuf);
929                 if(strncmp(newbuf,"tetrisstart ",12) == 0)
930                     goto cryptok;
932 #ifndef NO_BRUTE_FORCE_DECRYPTION
933                 /* The IP-based crypt does not work for clients behind NAT. So
934                  * help them by brute-forcing the crypt. This should not be
935                  * even noticeable unless you are running this under ucLinux on
936                  * some XT machine. */
937                 for (hashval = 0; hashval < 35956; hashval++) {
938                     sprintf(iphashbuf, "%d", hashval);
939                     decrypt_message(buf, newbuf, iphashbuf);
940                     if(strncmp(newbuf,"tetrisstart ",12) == 0)
941                         goto cryptok;
942                 } /* for (hashval) */
943 #endif
945                 if (strncmp(newbuf, "tetrisstart ", 12) != 0) {
946                     close(fd);
947                     player_socks[i] = -1;
948                     continue;
949                 }
951 cryptok:
952                 /* Buffers should be the same size, but let's be paranoid */
953                 strncpy(buf, newbuf, sizeof(buf));
954                 buf[sizeof(buf)-1] = 0;
955             } /* if encrypted */
956             player_socks[i] = fd;  /* Has now registered */
957         } /* if client not registered */
959         if (!server_parse(i+1, buf)) {
960             close(fd);
961             player_socks[i] = -1;
962             if (players[i]) {
963                 send_to_all("playerleave %d", i+1);
964                 if (playing_game)
965                     player_loses(i+1);
966                 free(players[i]);
967                 players[i] = NULL;
968                 if (teams[i]) {
969                     free(teams[i]);
970                     teams[i] = NULL;
971                 }
972             }
973         }
974     } /* for each player socket */
977 /*************************************************************************/
979 #ifdef SERVER_ONLY
980 int main()
981 #else
982 int server_main()
983 #endif
985     int i;
987     if ((i = init()) != 0)
988         return i;
989     while (!quit)
990         check_sockets();
991     write_config();
992     if (listen_sock >= 0)
993         close(listen_sock);
994 #ifdef HAVE_IPV6
995     if (listen_sock6 >= 0)
996         close(listen_sock6);
997 #endif
998     for (i = 0; i < 6; i++)
999         close(player_socks[i]);
1000     return 0;
1003 /*************************************************************************/