1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2  * This program is public domain.
3  *
4  * Tetrinet server code
5  */
6 
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"
26 
27 /*************************************************************************/
28 
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) */
31 
32 static int quit = 0;
33 
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];
41 
42 /* Which players have already lost in the current game? */
43 static int player_lost[6];
44 
45 /* We re-use a lot of variables from the main code */
46 
47 /*************************************************************************/
48 /*************************************************************************/
49 
50 /* Convert a 2-byte hex value to an integer. */
51 
xtoi(const char * buf)52 int xtoi(const char *buf)
53 {
54     int val;
55 
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;
65 }
66 
67 /*************************************************************************/
68 
69 /* Return a string containing the winlist in a format suitable for sending
70  * to clients.
71  */
72 
winlist_str()73 static char *winlist_str()
74 {
75     static char buf[1024];
76     char *s;
77     int i;
78 
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;
87 }
88 
89 /*************************************************************************/
90 /*************************************************************************/
91 
92 /* Read the configuration file. */
93 
read_config(void)94 void read_config(void)
95 {
96     char buf[1024], *s, *t;
97     FILE *f;
98     int i;
99 
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);
177 }
178 
179 /*************************************************************************/
180 
181 /* Re-write the configuration file. */
182 
write_config(void)183 void write_config(void)
184 {
185     char buf[1024], *s;
186     FILE *f;
187     int i;
188 
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;
195 
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);
202 
203     fprintf(f, "classic %d\n", old_mode);
204 
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);
209 
210     fprintf(f, "speciallines %d\n", special_lines);
211     fprintf(f, "specialcount %d\n", special_count);
212     fprintf(f, "specialcapacity %d\n", special_capacity);
213 
214     fprintf(f, "pieces");
215     for (i = 0; i < 7; i++)
216 	fprintf(f, " %d", piecefreq[i]);
217     fputc('\n', f);
218 
219     fprintf(f, "specials");
220     for (i = 0; i < 9; i++)
221 	fprintf(f, " %d", specialfreq[i]);
222     fputc('\n', f);
223 
224     fprintf(f, "linuxmode %d\n", linuxmode);
225     fprintf(f, "ipv6_only %d\n", ipv6_only);
226 
227     fclose(f);
228 }
229 
230 /*************************************************************************/
231 /*************************************************************************/
232 
233 /* Send a message to a single player. */
234 
send_to(int player,const char * format,...)235 static void send_to(int player, const char *format, ...)
236 {
237     va_list args;
238     char buf[1024];
239 
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);
244 }
245 
246 /*************************************************************************/
247 
248 /* Send a message to all players. */
249 
send_to_all(const char * format,...)250 static void send_to_all(const char *format, ...)
251 {
252     va_list args;
253     char buf[1024];
254     int i;
255 
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     }
262 }
263 
264 /*************************************************************************/
265 
266 /* Send a message to all players but the given one. */
267 
send_to_all_but(int player,const char * format,...)268 static void send_to_all_but(int player, const char *format, ...)
269 {
270     va_list args;
271     char buf[1024];
272     int i;
273 
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     }
280 }
281 
282 /*************************************************************************/
283 
284 /* Send a message to all players but those on the same team as the given
285  * player.
286  */
287 
send_to_all_but_team(int player,const char * format,...)288 static void send_to_all_but_team(int player, const char *format, ...)
289 {
290     va_list args;
291     char buf[1024];
292     int i;
293     char *team = teams[player-1];
294 
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     }
302 }
303 
304 /*************************************************************************/
305 /*************************************************************************/
306 
307 /* Add points to a given player's [team's] winlist entry, or make a new one
308  * if they rank.
309  */
310 
add_points(int player,int points)311 static void add_points(int player, int points)
312 {
313     int i;
314 
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;
343 }
344 
345 /*************************************************************************/
346 
347 /* Add a game to a given player's [team's] winlist entry. */
348 
add_game(int player)349 static void add_game(int player)
350 {
351     int i;
352 
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++;
366 }
367 
368 /*************************************************************************/
369 
370 /* Sort the winlist. */
371 
sort_winlist()372 static void sort_winlist()
373 {
374     int i, j, best, bestindex;
375 
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     }
392 }
393 
394 /*************************************************************************/
395 
396 /* Take care of a player losing (which may end the game). */
397 
player_loses(int player)398 static void player_loses(int player)
399 {
400     int i, j, order, end = 1, winner = -1, second = -1, third = -1;
401 
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;
476 }
477 
478 /*************************************************************************/
479 /*************************************************************************/
480 
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  */
484 
server_parse(int player,char * buf)485 static int server_parse(int player, char *buf)
486 {
487     char *cmd, *s, *t;
488     int i, tetrifast = 0;
489 
490     cmd = strtok(buf, " ");
491 
492     if (!cmd) {
493 	return 1;
494 
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]);
525 
526     } else if (strcmp(cmd, "tetrifaster") == 0) {
527 	tetrifast = 1;
528 	goto newplayer;
529 
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 : "");
542 
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);
551 
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);
560 
561     } else if (strcmp(cmd, "startgame") == 0) {
562 	int total;
563 	char piecebuf[101], specialbuf[101];
564 
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));
616 
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);
630 
631     } else if (strcmp(cmd, "playerlost") == 0) {
632 	if (!(s = strtok(NULL, " ")) || atoi(s) != player)
633 	    return 1;
634 	player_loses(player);
635 
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);
642 
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]);
650 
651     } else if (strcmp(cmd, "sb") == 0) {
652 	int from, to;
653 	char *type;
654 
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);
671 
672     } else if (strcmp(cmd, "gmsg") == 0) {
673 	if (!(s = strtok(NULL, "")))
674 	    return 1;
675 	send_to_all("gmsg %s", s);
676 
677     } else {  /* unrecognized command */
678 	return 0;
679 
680     }
681 
682     return 1;
683 }
684 
685 /*************************************************************************/
686 /*************************************************************************/
687 
sigcatcher(int sig)688 static void sigcatcher(int sig)
689 {
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     }
698 }
699 
700 /*************************************************************************/
701 
702 /* Returns 0 on success, desired program exit code on failure */
703 
init()704 static int init()
705 {
706     struct sockaddr_in sin;
707 #ifdef HAVE_IPV6
708     struct sockaddr_in6 sin6;
709 #endif
710     int i;
711 
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;
738 
739     /* (Try to) read the config file */
740     read_config();
741 
742     /* Catch some signals */
743     signal(SIGHUP, sigcatcher);
744     signal(SIGINT, sigcatcher);
745     signal(SIGTERM, sigcatcher);
746 
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:
768 
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 */
796 
797     if (listen_sock < 0
798 #ifdef HAVE_IPV6
799      && listen_sock6 < 0
800 #endif
801     ) {
802 	return 1;
803     }
804 
805     return 0;
806 }
807 
808 /*************************************************************************/
809 
check_sockets()810 static void check_sockets()
811 {
812     fd_set fds;
813     int i, fd, maxfd;
814 
815     FD_ZERO(&fds);
816     if (listen_sock >= 0)
817 	FD_SET(listen_sock, &fds);
818     maxfd = listen_sock;
819 #ifdef HAVE_IPV6
820     if (listen_sock6 >= 0)
821 	FD_SET(listen_sock6, &fds);
822     if (listen_sock6 > maxfd)
823 	maxfd = listen_sock6;
824 #endif
825     for (i = 0; i < 6; i++) {
826 	if (player_socks[i] != -1) {
827 	    if (player_socks[i] < 0)
828 		fd = (~player_socks[i]) - 1;
829 	    else
830 		fd = player_socks[i];
831 	    FD_SET(fd, &fds);
832 	    if (fd > maxfd)
833 		maxfd = fd;
834 	}
835     }
836 
837     if (select(maxfd+1, &fds, NULL, NULL, NULL) <= 0)
838 	return;
839 
840     if (listen_sock >= 0 && FD_ISSET(listen_sock, &fds)) {
841 	struct sockaddr_in sin;
842 	socklen_t len = sizeof(sin);
843 	fd = accept(listen_sock, (struct sockaddr *)&sin, &len);
844 	if (fd >= 0) {
845 	    for (i = 0; i < 6 && player_socks[i] != -1; i++)
846 		;
847 	    if (i == 6) {
848 		sockprintf(fd, "noconnecting Too many players on server!");
849 		close(fd);
850 	    } else {
851 		player_socks[i] = ~(fd+1);
852 		memcpy(player_ips[i], &sin.sin_addr, 4);
853 	    }
854 	}
855     } /* if (FD_ISSET(listen_sock)) */
856 
857 #ifdef HAVE_IPV6
858     if (listen_sock6 >= 0 && FD_ISSET(listen_sock6, &fds)) {
859 	struct sockaddr_in6 sin6;
860 	socklen_t len = sizeof(sin6);
861 	fd = accept(listen_sock6, (struct sockaddr *)&sin6, &len);
862 	if (fd >= 0) {
863 	    for (i = 0; i < 6 && player_socks[i] != -1; i++)
864 		;
865 	    if (i == 6) {
866 		sockprintf(fd, "noconnecting Too many players on server!");
867 		close(fd);
868 	    } else {
869 		player_socks[i] = ~(fd+1);
870 		memcpy(player_ips[i], (char *)(&sin6.sin6_addr)+12, 4);
871 	    }
872 	}
873     } /* if (FD_ISSET(listen_sock6)) */
874 #endif
875 
876     for (i = 0; i < 6; i++) {
877 	char buf[1024];
878 
879 	if (player_socks[i] == -1)
880 	    continue;
881 	if (player_socks[i] < 0)
882 	    fd = (~player_socks[i]) - 1;
883 	else
884 	    fd = player_socks[i];
885 	if (!FD_ISSET(fd, &fds))
886 	    continue;
887 	sgets(buf, sizeof(buf), fd);
888 	if (player_socks[i] < 0) {
889 	    /* Messy decoding stuff */
890 	    char iphashbuf[16], newbuf[1024];
891 	    unsigned char *ip;
892 	    int j, c, d;
893 
894 	    if (strlen(buf) < 2*13) {	/* "tetrisstart " + initial byte */
895 		close(fd);
896 		player_socks[i] = -1;
897 		continue;
898 	    }
899 	    ip = player_ips[i];
900 	    sprintf(iphashbuf, "%d", ip[0]*54 + ip[1]*41 + ip[2]*29 + ip[3]*17);
901 	    c = xtoi(buf);
902 	    for (j = 2; buf[j] && buf[j+1]; j += 2) {
903 		int temp;
904 		temp = d = xtoi(buf+j);
905 		d ^= iphashbuf[((j/2)-1) % strlen(iphashbuf)];
906 		d += 255 - c;
907 		d %= 255;
908 		newbuf[j/2-1] = d;
909 		c = temp;
910 	    }
911 	    newbuf[j/2-1] = 0;
912 	    if (strncmp(newbuf, "tetrisstart ", 12) != 0) {
913 		close(fd);
914 		player_socks[i] = -1;
915 		continue;
916 	    }
917 	    /* Buffers should be the same size, but let's be paranoid */
918 	    strncpy(buf, newbuf, sizeof(buf));
919 	    buf[sizeof(buf)-1] = 0;
920 	    player_socks[i] = fd;  /* Has now registered */
921 	} /* if client not registered */
922 	if (!server_parse(i+1, buf)) {
923 	    close(fd);
924 	    player_socks[i] = -1;
925 	    if (players[i]) {
926 		send_to_all("playerleave %d", i+1);
927 		if (playing_game)
928 		    player_loses(i+1);
929 		free(players[i]);
930 		players[i] = NULL;
931 		if (teams[i]) {
932 		    free(teams[i]);
933 		    teams[i] = NULL;
934 		}
935 	    }
936 	}
937     } /* for each player socket */
938 }
939 
940 /*************************************************************************/
941 
942 #ifdef SERVER_ONLY
main()943 int main()
944 #else
945 int server_main()
946 #endif
947 {
948     int i;
949 
950     if ((i = init()) != 0)
951 	return i;
952     while (!quit)
953 	check_sockets();
954     write_config();
955     if (listen_sock >= 0)
956 	close(listen_sock);
957 #ifdef HAVE_IPV6
958     if (listen_sock6 >= 0)
959 	close(listen_sock6);
960 #endif
961     for (i = 0; i < 6; i++)
962 	close(player_socks[i]);
963     return 0;
964 }
965 
966 /*************************************************************************/
967