DEADSOFTWARE

Net: improving anti-autism measures by travi$
[d2df-sdl.git] / src / mastersrv / master.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <ctype.h>
4 #include <string.h>
5 #include <time.h>
7 #include <enet/enet.h>
8 #include <enet/types.h>
10 #define MS_VERSION "0.2"
11 #define MS_MAXSRVS 128
12 #define MS_MAXHOST 5
13 #define MS_MAXBANS 256
14 #define MS_TIMEOUT 100
15 #define MS_BANTIME (3 * 86400)
16 #define MS_MAXHEUR 100
18 #define NET_CHANS 2
19 #define NET_CH_MAIN 0
20 #define NET_CH_UPD 1
21 #define NET_MAXCLIENTS 64
23 #define NET_BUFSIZE 65536
25 #define NET_MSG_ADD 200
26 #define NET_MSG_RM 201
27 #define NET_MSG_LIST 202
29 #define LC_MS_INIT "D2DF master server starting on port %d...\n"
30 #define LC_MS_ADD "\nAdded server in slot #%d:\n%s:%d\n%s\n%s (%d)\n%d/%d plrs\nproto: %d pw?: %d\n"
31 #define LC_MS_UPD "\nUpdated server #%d (%s:%d):\n%s\n%s (%d)\n%d/%d plrs\nproto: %d pw?: %d\n"
32 #define LC_MS_RM "\nRemoved server #%d (%s:%d) by request.\n"
33 #define LC_MS_TIME "\nServer #%d (%s:%d) timed out.\n"
34 #define LC_MS_LIST "\nSent server list to %x:%u (ver. %s).\n"
35 #define LC_MS_DIE "\nD2DF master server shutting down...\n"
36 #define LC_MS_CONN "\nIncoming connection from %x:%u...\n"
37 #define LC_MS_MOTD "\nMOTD: %s\n"
38 #define LC_MS_URGENT "\nURGENT: %s\n"
39 #define LC_MS_BANNED "\nBanned %s until %s, reason: %s (#%d)\n"
40 #define LC_MS_NOBANS "\nCould not load ban list from file\n"
41 #define LC_MS_BADADR "\nBad address in file: %s\n"
42 #define LC_MS_BANHEUR "tripped heuristic check"
43 #define LC_MS_BANTOOMUCH "created too many servers"
44 #define LC_MS_BANSPAM "suspicious multiple server activity"
45 #define LC_MS_BANLIST "address in ban list"
46 #define LC_MS_OOM "\nOut of memory\n"
48 #define MS_URGENT_FILE "urgent.txt"
49 #define MS_MOTD_FILE "motd.txt"
50 #define MS_BAN_FILE "master_bans.txt"
52 struct ms_ban_s;
54 typedef struct ms_ban_record_s {
55 ENetAddress mask;
56 char ip[18];
57 int ban_count;
58 time_t cur_ban;
59 struct ms_ban_record_s *next;
60 struct ms_ban_record_s *prev;
61 } ms_ban_record;
63 struct ms_server_s {
64 enet_uint8 used;
65 char s_ip[17];
66 char s_name[256];
67 char s_map[256];
68 enet_uint8 s_pw;
69 enet_uint8 s_plrs;
70 enet_uint8 s_maxplrs;
71 enet_uint8 s_mode;
72 enet_uint8 s_protocol;
73 enet_uint16 s_port;
74 time_t deathtime;
75 time_t lasttime;
76 };
78 typedef struct ms_server_s ms_server;
80 const char ms_game_ver[] = "0.63";
81 char ms_motd[255] = "";
82 char ms_urgent[255] = "";
84 int ms_port = 25660;
85 int ms_timeout = 100;
86 int ms_checkmultiple = 0;
88 size_t b_read = 0;
89 size_t b_write = 0;
91 enet_uint8 b_send[NET_BUFSIZE];
93 ENetHost *ms_host = NULL;
94 ENetPeer *ms_peers[NET_MAXCLIENTS];
96 ms_server ms_srv[MS_MAXSRVS];
97 enet_uint8 ms_count = 0;
99 ms_ban_record *ms_bans;
101 // fake servers to show on old versions of the game
102 static const ms_server ms_fake_srv[] = {
104 .used = 1,
105 .s_ip = "0.0.0.0",
106 .s_name = "! \xc2\xc0\xd8\xc0 \xca\xce\xcf\xc8\xdf \xc8\xc3\xd0\xdb "
107 "\xd3\xd1\xd2\xc0\xd0\xc5\xcb\xc0! "
108 "\xd1\xca\xc0\xd7\xc0\xc9\xd2\xc5 \xcd\xce\xc2\xd3\xde C "
109 "doom2d.org !",
110 .s_map = "! Your game is outdated. "
111 "Get latest version at doom2d.org !",
112 .s_protocol = 255,
113 },
115 .used = 1,
116 .s_ip = "0.0.0.0",
117 .s_name = "! \xcf\xd0\xce\xc1\xd0\xce\xd1\xdcTE \xcf\xce\xd0\xd2\xdb "
118 "25666 \xc8 57133 HA CEPBEPE \xcf\xc5\xd0\xc5\xc4 \xc8\xc3\xd0\xce\xc9 !",
119 .s_map = "! Forward ports 25666 and 57133 before hosting !",
120 .s_protocol = 255,
121 },
122 };
124 #define MS_FAKESRVS (sizeof(ms_fake_srv) / sizeof(ms_fake_srv[0]))
126 void i_usage (void) {
127 printf("Usage: d2df_master -p port_number [-t timeout_seconds]\n");
128 fflush(stdout);
132 void i_version (void) {
133 printf("Doom 2D Forever master server v%s\n", MS_VERSION);
134 fflush(stdout);
138 void d_error (const char *msg, int fatal) {
139 if (fatal) {
140 fprintf(stderr, "FATAL ERROR: %s\n", msg);
141 exit(EXIT_FAILURE);
142 } else {
143 fprintf(stderr, "ERROR: %s\n", msg);
148 void d_getargs (int argc, char *argv[]) {
149 if (argc < 2) {
150 i_usage();
151 exit(0);
152 return;
155 for (int i = 1; i < argc; ++i) {
156 if (!strcmp(argv[i], "-v")) {
157 i_version();
158 exit(0);
159 return;
160 } else if (!strcmp(argv[i], "-p")) {
161 if (i + 1 >= argc) {
162 d_error("Specify a port value!", 1);
163 return;
164 } else {
165 ms_port = atoi(argv[++i]);
167 } else if (!strcmp(argv[i], "-t") & (i + 1 < argc)) {
168 ms_timeout = atoi(argv[++i]);
169 } else if (!strcmp(argv[i], "--check-multihost")) {
170 ms_checkmultiple = 1;
177 int d_readtextfile (const char *fname, char *buf, size_t max) {
178 FILE *f = fopen(fname, "r");
179 char *const end = buf + max - 1;
180 char *p = buf;
181 if (f) {
182 char ln[max];
183 char *const lend = ln + max - 1;
184 while (p < end && fgets(ln, max, f)) {
185 for (char *n = ln; n < lend && *n && *n != '\r' && *n != '\n'; ++n) {
186 *(p++) = *n;
187 if (p == end) break;
190 *p = '\0';
191 fclose(f);
192 return 0;
194 return 1;
198 int d_strisprint (const char *str) {
199 if (!str || !*str) return 0;
200 for (const char *p = str; p && *p; ++p)
201 if (isprint(*p) || *p > 0x7f) return 1;
202 return 0;
206 const char *d_strtime(const time_t t) {
207 static char buf[128];
208 struct tm *ptm = localtime(&t);
209 strftime(buf, sizeof(buf), "%c", ptm);
210 return buf;
214 enet_uint8 b_read_uint8 (enet_uint8 buf[], size_t *pos) {
215 return buf[(*pos)++];
219 enet_uint16 b_read_uint16 (enet_uint8 buf[], size_t *pos) {
220 enet_uint16 ret = 0;
222 ret = *(enet_uint16*)(buf + *pos);
223 *pos += sizeof(enet_uint16);
225 return ret;
229 char* b_read_dstring (enet_uint8 buf[], size_t *pos) {
230 char *ret = NULL;
232 size_t len = b_read_uint8(buf, pos);
234 ret = malloc(len + 1);
236 memmove(ret, (char*)(buf + *pos), len);
237 ret[len] = '\0';
238 *pos += len;
240 return ret;
244 void b_write_uint8 (enet_uint8 buf[], size_t *pos, enet_uint8 val) {
245 buf[(*pos)++] = val;
249 void b_write_uint16 (enet_uint8 buf[], size_t *pos, enet_uint16 val) {
250 *(enet_uint16*)(buf + *pos) = val;
251 *pos += sizeof(enet_uint16);
255 void b_write_dstring (enet_uint8 buf[], size_t *pos, const char* val) {
256 enet_uint8 len = strlen(val);
257 b_write_uint8(buf, pos, len);
259 memmove((char*)(buf + *pos), val, len);
260 *pos += len;
264 void b_write_server (enet_uint8 buf[], size_t *pos, ms_server s) {
265 b_write_dstring(b_send, pos, s.s_ip);
266 b_write_uint16 (b_send, pos, s.s_port);
267 b_write_dstring(b_send, pos, s.s_name);
268 b_write_dstring(b_send, pos, s.s_map);
269 b_write_uint8 (b_send, pos, s.s_mode);
270 b_write_uint8 (b_send, pos, s.s_plrs);
271 b_write_uint8 (b_send, pos, s.s_maxplrs);
272 b_write_uint8 (b_send, pos, s.s_protocol);
273 b_write_uint8 (b_send, pos, s.s_pw);
277 time_t ban_get_time(const int cnt) {
278 static const time_t times[] = {
279 1 * 5 * 60,
280 1 * 30 * 60,
281 1 * 60 * 60,
282 24 * 60 * 60,
283 72 * 60 * 60,
284 8760 * 60 * 60,
285 };
287 static const size_t numtimes = sizeof(times) / sizeof(*times);
289 if (cnt >= numtimes || cnt < 0)
290 return times[numtimes - 1];
292 return times[cnt];
296 ms_ban_record *ban_check (const ENetAddress *addr) {
297 const time_t now = time(NULL);
299 for (ms_ban_record *b = ms_bans; b; b = b->next) {
300 if (b->mask.host == addr->host) {
301 if (b->cur_ban > now)
302 return b;
306 return NULL;
310 ms_ban_record *ban_record_check (const ENetAddress *addr) {
311 for (ms_ban_record *b = ms_bans; b; b = b->next) {
312 if (b->mask.host == addr->host)
313 return b;
315 return NULL;
319 ms_ban_record *ban_record_add_addr (const ENetAddress *addr, const int cnt, const time_t cur) {
320 ms_ban_record *rec = ban_record_check(addr);
321 if (rec) return rec;
323 rec = calloc(1, sizeof(*rec));
324 if (!rec) return NULL;
326 enet_address_get_host_ip(addr, rec->ip, 17);
327 rec->mask = *addr;
328 rec->ban_count = cnt;
329 rec->cur_ban = cur;
331 if (ms_bans) ms_bans->prev = rec;
332 rec->next = ms_bans;
333 ms_bans = rec;
335 return rec;
339 ms_ban_record *ban_record_add_ip (const char *ip, const int cnt, const time_t cur) {
340 ENetAddress addr;
341 if (enet_address_set_host_ip(&addr, ip) != 0) {
342 fprintf(stderr, LC_MS_BADADR, ip);
343 return NULL;
345 return ban_record_add_addr(&addr, cnt, cur);
349 void ban_load_list (const char *fname) {
350 FILE *f = fopen(fname, "r");
351 if (!f) {
352 d_error(LC_MS_NOBANS, 0);
353 return;
356 char ln[256] = { 0 };
358 while (fgets(ln, sizeof(ln), f)) {
359 for (int i = sizeof(ln) - 1; i >= 0; --i)
360 if (ln[i] == '\n' || ln[i] == '\r')
361 ln[i] = 0;
363 char ip[17] = { 0 };
364 time_t exp = 0;
365 int count = 0;
367 sscanf(ln, "%16s %ld %d", ip, &exp, &count);
369 if (ban_record_add_ip(ip, count, exp))
370 printf(LC_MS_BANNED, ip, d_strtime(exp), LC_MS_BANLIST, count);
373 fclose(f);
377 void ban_save_list (const char *fname) {
378 FILE *f = fopen(fname, "w");
379 if (!f) {
380 d_error(LC_MS_NOBANS, 0);
381 return;
384 for (ms_ban_record *rec = ms_bans; rec; rec = rec->next)
385 if (rec->ban_count)
386 fprintf(f, "%s %ld %d\n", rec->ip, rec->cur_ban, rec->ban_count);
388 fclose(f);
392 int ban_heur (const ms_server *srv, const time_t now) {
393 int score = 0;
395 // can't have more than 24 maxplayers; can't have more than max
396 if (srv->s_plrs > srv->s_maxplrs || srv->s_maxplrs > 24 || srv->s_maxplrs == 0)
397 score += MS_MAXHEUR;
399 // name and map have to be non-garbage
400 if (!d_strisprint(srv->s_map) || !d_strisprint(srv->s_name))
401 score += MS_MAXHEUR;
403 // these protocols don't exist
404 if (srv->s_protocol < 100 || srv->s_protocol > 250)
405 score += MS_MAXHEUR;
407 // the game doesn't allow server names longer than 64 chars
408 if (strlen(srv->s_name) > 64)
409 score += MS_MAXHEUR;
411 // game mode has to actually exist
412 if (srv->s_mode > 5)
413 score += MS_MAXHEUR;
415 // password field can be either 0 or 1
416 if (srv->s_pw > 1)
417 score += MS_MAXHEUR;
419 // port has to be set, although the game allows you to set it to 0
420 // if (!srv->s_port)
421 // score += MS_MAXHEUR;
423 // servers usually don't update more often than once every 30 seconds
424 if (now - srv->lasttime < 5)
425 score += MS_MAXHEUR / 2;
427 return score;
430 void erase_banned_host(char* ip) {
431 for (int i = 0; i < MS_MAXSRVS; ++i) {
432 if (ms_srv[i].s_ip == ip) {
433 ms_srv[i].used = 0;
438 time_t get_sum_lasttime(char* ip) {
439 time_t sumLastTime = 0;
440 const time_t now = time(NULL);
441 for (int i = 0; i < MS_MAXSRVS; ++i) {
442 if (ms_srv[i].used && (strncmp(ip, ms_srv[i].s_ip, 16) == 0)) {
443 sumLastTime = sumLastTime + (now - ms_srv[i].lasttime);
446 return sumLastTime;
449 void ban_add (const ENetAddress *addr, const char *reason) {
450 const time_t now = time(NULL);
452 ms_ban_record *rec = ban_record_add_addr(addr, 0, 0);
453 if (!rec) d_error(LC_MS_OOM, 1);
455 rec->cur_ban = now + ban_get_time(rec->ban_count);
456 rec->ban_count++;
458 printf(LC_MS_BANNED, rec->ip, d_strtime(rec->cur_ban), reason, rec->ban_count);
460 ban_save_list(MS_BAN_FILE);
461 erase_banned_host(rec->ip);
465 void d_deinit(void) {
466 ban_save_list(MS_BAN_FILE);
469 int count_servers(char* ip) {
470 int sameHostServers = 0;
471 for (int i = 0; i < MS_MAXSRVS; ++i) {
472 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0)) {
473 ++sameHostServers;
476 return sameHostServers;
482 int main (int argc, char *argv[]) {
483 d_getargs(argc, argv);
485 if (enet_initialize()) {
486 d_error("Could not init ENet!", 1);
487 return EXIT_FAILURE;
490 printf(LC_MS_INIT, ms_port);
492 d_readtextfile(MS_MOTD_FILE, ms_motd, sizeof(ms_motd));
493 d_readtextfile(MS_URGENT_FILE, ms_urgent, sizeof(ms_urgent));
494 ban_load_list(MS_BAN_FILE);
496 if (ms_motd[0]) printf(LC_MS_MOTD, ms_motd);
497 if (ms_urgent[0]) printf(LC_MS_URGENT, ms_urgent);
499 for (int i = 0; i < NET_MAXCLIENTS; ++i) ms_peers[i] = NULL;
501 for (int i = 0; i < MS_MAXSRVS; ++i) {
502 ms_srv[i].used = 0;
503 ms_srv[i].s_ip[0] = '\0';
504 ms_srv[i].s_name[0] = '\0';
505 ms_srv[i].s_map[0] = '\0';
506 ms_srv[i].deathtime = 0;
509 ENetAddress addr;
510 addr.host = ENET_HOST_ANY;
511 addr.port = ms_port;
513 ms_host = enet_host_create(&addr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
514 if (!ms_host) {
515 d_error("Could not create host on specified port!", 1);
516 return EXIT_FAILURE;
519 atexit(d_deinit);
521 ENetEvent event;
522 int shutdown = 0;
523 enet_uint8 msg = 255;
525 char ip[17];
526 enet_uint16 port = 0;
528 char *name = NULL;
529 char *map = NULL;
530 char *clientver = NULL;
531 enet_uint8 gm = 0;
532 enet_uint16 pl = 0;
533 enet_uint16 mpl = 0;
535 enet_uint8 proto = 0;
536 enet_uint8 pw = 0;
537 while (!shutdown) {
538 while (enet_host_service(ms_host, &event, 5000) > 0) {
539 if (event.peer && ban_check(&(event.peer->address)))
540 continue;
542 const time_t now = time(NULL);
544 switch (event.type) {
545 case ENET_EVENT_TYPE_CONNECT:
546 printf(LC_MS_CONN, event.peer->address.host, event.peer->address.port);
547 break;
549 case ENET_EVENT_TYPE_RECEIVE:
550 if (!event.peer) continue;
552 b_read = 0;
553 msg = b_read_uint8(event.packet->data, &b_read);
555 switch (msg) {
556 case NET_MSG_ADD:
557 enet_address_get_host_ip(&(event.peer->address), ip, 17);
558 port = b_read_uint16(event.packet->data, &b_read);
560 name = b_read_dstring(event.packet->data, &b_read);
561 map = b_read_dstring(event.packet->data, &b_read);
562 gm = b_read_uint8(event.packet->data, &b_read);
564 pl = b_read_uint8(event.packet->data, &b_read);
565 mpl = b_read_uint8(event.packet->data, &b_read);
567 proto = b_read_uint8(event.packet->data, &b_read);
568 pw = b_read_uint8(event.packet->data, &b_read);
570 for (int i = 0; i < MS_MAXSRVS; ++i) {
571 if (ms_srv[i].used) {
572 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
573 if (ban_heur(ms_srv + i, now) >= MS_MAXHEUR) {
574 ban_add(&(event.peer->address), LC_MS_BANHEUR);
575 break;
578 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
579 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
580 ms_srv[i].s_plrs = pl;
581 ms_srv[i].s_maxplrs = mpl;
582 ms_srv[i].s_pw = pw;
583 ms_srv[i].s_mode = gm;
585 ms_srv[i].deathtime = now + ms_timeout;
586 ms_srv[i].lasttime = now;
588 printf(LC_MS_UPD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
589 break;
591 } else {
592 int countServer = count_servers(ip);
593 if (countServer > MS_MAXHOST) {
594 ban_add(&(event.peer->address), LC_MS_BANTOOMUCH);
595 break;
597 else if (ms_checkmultiple && countServer > 1) {
598 if (get_sum_lasttime(ip) < (countServer*3)) {
599 ban_add(&(event.peer->address), LC_MS_BANSPAM);
600 break;
603 strncpy(ms_srv[i].s_ip, ip, sizeof(ms_srv[i].s_ip));
604 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
605 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
606 ms_srv[i].s_port = port;
607 ms_srv[i].s_plrs = pl;
608 ms_srv[i].s_maxplrs = mpl;
609 ms_srv[i].s_pw = pw;
610 ms_srv[i].s_mode = gm;
611 ms_srv[i].s_protocol = proto;
612 ms_srv[i].deathtime = now + ms_timeout;
613 ms_srv[i].lasttime = now;
615 if (ban_heur(ms_srv + i, now) >= MS_MAXHEUR) {
616 ban_add(&(event.peer->address), LC_MS_BANHEUR);
617 break;
620 ms_srv[i].used = 1;
622 printf(LC_MS_ADD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
624 ++ms_count;
625 break;
628 free(name);
629 free(map);
630 break;
632 case NET_MSG_RM:
633 enet_address_get_host_ip(&(event.peer->address), ip, 17);
634 port = b_read_uint16(event.packet->data, &b_read);
635 for (int i = 0; i < MS_MAXSRVS; ++i) {
636 if (ms_srv[i].used) {
637 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
638 if (ban_heur(ms_srv + i, now) >= MS_MAXHEUR) {
639 ban_add(&(event.peer->address), LC_MS_BANHEUR);
640 break;
642 ms_srv[i].used = 0;
643 printf(LC_MS_RM, i, ip, port);
644 --ms_count;
648 break;
650 case NET_MSG_LIST:
651 b_write = 0;
652 b_write_uint8(b_send, &b_write, NET_MSG_LIST);
654 if (event.packet->dataLength > 2) {
655 // holy shit a fresh client
656 clientver = b_read_dstring(event.packet->data, &b_read);
657 b_write_uint8(b_send, &b_write, ms_count);
658 } else {
659 // old client, feed them bullshit first
660 b_write_uint8(b_send, &b_write, ms_count + 2);
661 for (int i = 0; i < MS_FAKESRVS; ++i)
662 b_write_server(b_send, &b_write, ms_fake_srv[i]);
665 for (int i = 0; i < MS_MAXSRVS; ++i) {
666 if (ms_srv[i].used) b_write_server(b_send, &b_write, ms_srv[i]);
669 if (clientver) {
670 // TODO: check if this client is outdated (?) and send back new verstring
671 // for now just write the same shit back
672 b_write_dstring(b_send, &b_write, clientver);
673 // write the motd and urgent message
674 b_write_dstring(b_send, &b_write, ms_motd);
675 b_write_dstring(b_send, &b_write, ms_urgent);
678 ENetPacket *p = enet_packet_create(b_send, b_write, ENET_PACKET_FLAG_RELIABLE);
679 enet_peer_send(event.peer, NET_CH_MAIN, p);
680 enet_host_flush(ms_host);
682 printf(LC_MS_LIST, event.peer->address.host, event.peer->address.port, clientver ? clientver : "<old>");
683 free(clientver);
684 clientver = NULL;
685 break;
688 enet_packet_destroy(event.packet);
689 break;
691 default:
692 break;
696 time_t now = time(NULL);
697 for (int i = 0; i < MS_MAXSRVS; ++i) {
698 if (ms_srv[i].used) {
699 if (ms_srv[i].deathtime <= now) {
700 ms_srv[i].used = 0;
701 printf(LC_MS_TIME, i, ms_srv[i].s_ip, ms_srv[i].s_port);
702 --ms_count;
708 printf(LC_MS_DIE);
710 return EXIT_SUCCESS;