ChangeLog update
[tetrinet.git] / server.c
blob82c4f64eeff97bae7fafbc91b3a75459890f3802
1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
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.
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);
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;
163 winlist[i].team = atoi(s);
164 s = t+1;
165 t = strchr(s, ';');
166 if (!t) {
167 *winlist[i].name = 0;
168 break;
170 winlist[i].points = atoi(s);
171 winlist[i].games = atoi(t+1);
172 i++;
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);
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);
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);
282 /*************************************************************************/
284 /* Send a message to all players but those on the same team as the given
285 * player.
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);
304 /*************************************************************************/
305 /*************************************************************************/
307 /* Add points to a given player's [team's] winlist entry, or make a new one
308 * if they rank.
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;
325 if (i == MAXWINLIST) {
326 for (i = 0; i < MAXWINLIST && winlist[i].points >= points; i++)
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;
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;
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;
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));
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];
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;
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;
437 if (order) {
438 add_points(second, 2);
439 player_lost[second-1] = 0;
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;
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;
460 if (j < i)
461 continue;
463 if (player_socks[i-1] >= 0)
464 add_game(i);
467 sort_winlist();
468 write_config();
469 send_to_all("winlist %s", winlist_str());
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.
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;
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] : "");
520 if (playing_game) {
521 send_to(player, "ingame");
522 player_lost[player-1] = 1;
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;
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;
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];
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;
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];
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;
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);
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;
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);
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;
762 i = errno;
763 close(listen_sock);
764 errno = i;
765 listen_sock = -1;
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;
784 i = errno;
785 close(listen_sock6);
786 errno = i;
787 listen_sock6 = -1;
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;
795 #endif /* HAVE_IPV6 */
797 if (listen_sock < 0
798 #ifdef HAVE_IPV6
799 && listen_sock6 < 0
800 #endif
802 return 1;
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;
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;
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++)
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);
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++)
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);
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;
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;
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;
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 /*************************************************************************/