DEADSOFTWARE

e910528b02de8536ee3b34264112f6b56a6250a3
[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_MAXBANS 256
13 #define MS_TIMEOUT 100
14 #define MS_BANTIME (3 * 86400)
15 #define MS_MAXHEUR 100
17 #define NET_CHANS 2
18 #define NET_CH_MAIN 0
19 #define NET_CH_UPD 1
20 #define NET_MAXCLIENTS 64
22 #define NET_BUFSIZE 65536
24 #define NET_MSG_ADD 200
25 #define NET_MSG_RM 201
26 #define NET_MSG_LIST 202
28 #define LC_MS_INIT "D2DF master server starting on port %d...\n"
29 #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"
30 #define LC_MS_UPD "\nUpdated server #%d (%s:%d):\n%s\n%s (%d)\n%d/%d plrs\nproto: %d pw?: %d\n"
31 #define LC_MS_RM "\nRemoved server #%d (%s:%d) by request.\n"
32 #define LC_MS_TIME "\nServer #%d (%s:%d) timed out.\n"
33 #define LC_MS_LIST "\nSent server list to %x:%u (ver. %s).\n"
34 #define LC_MS_DIE "\nD2DF master server shutting down...\n"
35 #define LC_MS_CONN "\nIncoming connection from %x:%u...\n"
36 #define LC_MS_MOTD "\nMOTD: %s\n"
37 #define LC_MS_URGENT "\nURGENT: %s\n"
38 #define LC_MS_BANNED "\nBanned %s until %s, reason: %s (#%d)\n"
39 #define LC_MS_NOBANS "\nCould not load ban list from file\n"
40 #define LC_MS_BADADR "\nBad address in file: %s\n"
41 #define LC_MS_BANHEUR "tripped heuristic check"
42 #define LC_MS_BANLIST "address in ban list"
43 #define LC_MS_OOM "\nOut of memory\n"
45 #define MS_URGENT_FILE "urgent.txt"
46 #define MS_MOTD_FILE "motd.txt"
47 #define MS_BAN_FILE "master_bans.txt"
49 struct ms_ban_s;
51 typedef struct ms_ban_record_s {
52 ENetAddress mask;
53 char ip[18];
54 int ban_count;
55 time_t cur_ban;
56 struct ms_ban_record_s *next;
57 struct ms_ban_record_s *prev;
58 } ms_ban_record;
60 struct ms_server_s {
61 enet_uint8 used;
62 char s_ip[17];
63 char s_name[256];
64 char s_map[256];
65 enet_uint8 s_pw;
66 enet_uint8 s_plrs;
67 enet_uint8 s_maxplrs;
68 enet_uint8 s_mode;
69 enet_uint8 s_protocol;
70 enet_uint16 s_port;
71 time_t deathtime;
72 time_t lasttime;
73 };
75 typedef struct ms_server_s ms_server;
77 const char ms_game_ver[] = "0.63";
78 char ms_motd[255] = "";
79 char ms_urgent[255] = "";
81 int ms_port = 25660;
82 int ms_timeout = 100;
84 size_t b_read = 0;
85 size_t b_write = 0;
87 enet_uint8 b_send[NET_BUFSIZE];
89 ENetHost *ms_host = NULL;
90 ENetPeer *ms_peers[NET_MAXCLIENTS];
92 ms_server ms_srv[MS_MAXSRVS];
93 enet_uint8 ms_count = 0;
95 ms_ban_record *ms_bans;
97 // fake servers to show on old versions of the game
98 static const ms_server ms_fake_srv[] = {
99 {
100 .used = 1,
101 .s_ip = "0.0.0.0",
102 .s_name = "! \xc2\xc0\xd8\xc0 \xca\xce\xcf\xc8\xdf \xc8\xc3\xd0\xdb "
103 "\xd3\xd1\xd2\xc0\xd0\xc5\xcb\xc0! "
104 "\xd1\xca\xc0\xd7\xc0\xc9\xd2\xc5 \xcd\xce\xc2\xd3\xde C "
105 "doom2d.org !",
106 .s_map = "! Your game is outdated. "
107 "Get latest version at doom2d.org !",
108 .s_protocol = 255,
109 },
111 .used = 1,
112 .s_ip = "0.0.0.0",
113 .s_name = "! \xcf\xd0\xce\xc1\xd0\xce\xd1\xdcTE \xcf\xce\xd0\xd2\xdb "
114 "25666 \xc8 57133 HA CEPBEPE \xcf\xc5\xd0\xc5\xc4 \xc8\xc3\xd0\xce\xc9 !",
115 .s_map = "! Forward ports 25666 and 57133 before hosting !",
116 .s_protocol = 255,
117 },
118 };
120 #define MS_FAKESRVS (sizeof(ms_fake_srv) / sizeof(ms_fake_srv[0]))
122 void i_usage (void) {
123 printf("Usage: d2df_master -p port_number [-t timeout_seconds]\n");
124 fflush(stdout);
128 void i_version (void) {
129 printf("Doom 2D Forever master server v%s\n", MS_VERSION);
130 fflush(stdout);
134 void d_error (const char *msg, int fatal) {
135 if (fatal) {
136 fprintf(stderr, "FATAL ERROR: %s\n", msg);
137 exit(EXIT_FAILURE);
138 } else {
139 fprintf(stderr, "ERROR: %s\n", msg);
144 void d_getargs (int argc, char *argv[]) {
145 if (argc < 2) {
146 i_usage();
147 exit(0);
148 return;
151 for (int i = 1; i < argc; ++i) {
152 if (!strcmp(argv[i], "-v")) {
153 i_version();
154 exit(0);
155 return;
156 } else if (!strcmp(argv[i], "-p")) {
157 if (i + 1 >= argc) {
158 d_error("Specify a port value!", 1);
159 return;
160 } else {
161 ms_port = atoi(argv[++i]);
163 } else if (!strcmp(argv[i], "-t") & (i + 1 < argc)) {
164 ms_timeout = atoi(argv[++i]);
170 int d_readtextfile (const char *fname, char *buf, size_t max) {
171 FILE *f = fopen(fname, "r");
172 char *const end = buf + max - 1;
173 char *p = buf;
174 if (f) {
175 char ln[max];
176 char *const lend = ln + max - 1;
177 while (p < end && fgets(ln, max, f)) {
178 for (char *n = ln; n < lend && *n && *n != '\r' && *n != '\n'; ++n) {
179 *(p++) = *n;
180 if (p == end) break;
183 *p = '\0';
184 fclose(f);
185 return 0;
187 return 1;
191 int d_strisprint (const char *str) {
192 if (!str || !*str) return 0;
193 for (const char *p = str; p && *p; ++p)
194 if (isprint(*p) || *p > 0x7f) return 1;
195 return 0;
199 const char *d_strtime(const time_t t) {
200 static char buf[128];
201 struct tm *ptm = localtime(&t);
202 strftime(buf, sizeof(buf), "%c", ptm);
203 return buf;
207 enet_uint8 b_read_uint8 (enet_uint8 buf[], size_t *pos) {
208 return buf[(*pos)++];
212 enet_uint16 b_read_uint16 (enet_uint8 buf[], size_t *pos) {
213 enet_uint16 ret = 0;
215 ret = *(enet_uint16*)(buf + *pos);
216 *pos += sizeof(enet_uint16);
218 return ret;
222 char* b_read_dstring (enet_uint8 buf[], size_t *pos) {
223 char *ret = NULL;
225 size_t len = b_read_uint8(buf, pos);
227 ret = malloc(len + 1);
229 memmove(ret, (char*)(buf + *pos), len);
230 ret[len] = '\0';
231 *pos += len;
233 return ret;
237 void b_write_uint8 (enet_uint8 buf[], size_t *pos, enet_uint8 val) {
238 buf[(*pos)++] = val;
242 void b_write_uint16 (enet_uint8 buf[], size_t *pos, enet_uint16 val) {
243 *(enet_uint16*)(buf + *pos) = val;
244 *pos += sizeof(enet_uint16);
248 void b_write_dstring (enet_uint8 buf[], size_t *pos, const char* val) {
249 enet_uint8 len = strlen(val);
250 b_write_uint8(buf, pos, len);
252 memmove((char*)(buf + *pos), val, len);
253 *pos += len;
257 void b_write_server (enet_uint8 buf[], size_t *pos, ms_server s) {
258 b_write_dstring(b_send, pos, s.s_ip);
259 b_write_uint16 (b_send, pos, s.s_port);
260 b_write_dstring(b_send, pos, s.s_name);
261 b_write_dstring(b_send, pos, s.s_map);
262 b_write_uint8 (b_send, pos, s.s_mode);
263 b_write_uint8 (b_send, pos, s.s_plrs);
264 b_write_uint8 (b_send, pos, s.s_maxplrs);
265 b_write_uint8 (b_send, pos, s.s_protocol);
266 b_write_uint8 (b_send, pos, s.s_pw);
270 time_t ban_get_time(const int cnt) {
271 static const time_t times[] = {
272 1 * 5 * 60,
273 1 * 30 * 60,
274 1 * 60 * 60,
275 24 * 60 * 60,
276 72 * 60 * 60,
277 8760 * 60 * 60,
278 };
280 static const size_t numtimes = sizeof(times) / sizeof(*times);
282 if (cnt >= numtimes || cnt < 0)
283 return times[numtimes - 1];
285 return times[cnt];
289 ms_ban_record *ban_check (const ENetAddress *addr) {
290 const time_t now = time(NULL);
292 for (ms_ban_record *b = ms_bans; b; b = b->next) {
293 if (b->mask.host == addr->host) {
294 if (b->cur_ban > now)
295 return b;
299 return NULL;
303 ms_ban_record *ban_record_check (const ENetAddress *addr) {
304 for (ms_ban_record *b = ms_bans; b; b = b->next) {
305 if (b->mask.host == addr->host)
306 return b;
308 return NULL;
312 ms_ban_record *ban_record_add_addr (const ENetAddress *addr, const int cnt, const time_t cur) {
313 ms_ban_record *rec = ban_record_check(addr);
314 if (rec) return rec;
316 rec = calloc(1, sizeof(*rec));
317 if (!rec) return NULL;
319 enet_address_get_host_ip(addr, rec->ip, 17);
320 rec->mask = *addr;
321 rec->ban_count = cnt;
322 rec->cur_ban = cur;
324 if (ms_bans) ms_bans->prev = rec;
325 rec->next = ms_bans;
326 ms_bans = rec;
328 return rec;
332 ms_ban_record *ban_record_add_ip (const char *ip, const int cnt, const time_t cur) {
333 ENetAddress addr;
334 if (enet_address_set_host_ip(&addr, ip) != 0) {
335 fprintf(stderr, LC_MS_BADADR, ip);
336 return NULL;
338 return ban_record_add_addr(&addr, cnt, cur);
342 void ban_load_list (const char *fname) {
343 FILE *f = fopen(fname, "r");
344 if (!f) {
345 d_error(LC_MS_NOBANS, 0);
346 return;
349 char ln[256] = { 0 };
351 while (fgets(ln, sizeof(ln), f)) {
352 for (int i = sizeof(ln) - 1; i >= 0; --i)
353 if (ln[i] == '\n' || ln[i] == '\r')
354 ln[i] = 0;
356 char ip[17] = { 0 };
357 time_t exp = 0;
358 int count = 0;
360 sscanf(ln, "%16s %ld %d", ip, &exp, &count);
362 if (ban_record_add_ip(ip, count, exp))
363 printf(LC_MS_BANNED, ip, d_strtime(exp), LC_MS_BANLIST, count);
366 fclose(f);
370 void ban_save_list (const char *fname) {
371 FILE *f = fopen(fname, "w");
372 if (!f) {
373 d_error(LC_MS_NOBANS, 0);
374 return;
377 for (ms_ban_record *rec = ms_bans; rec; rec = rec->next)
378 if (rec->ban_count)
379 fprintf(f, "%s %ld %d\n", rec->ip, rec->cur_ban, rec->ban_count);
381 fclose(f);
385 int ban_heur (const ms_server *srv, const time_t now) {
386 int score = 0;
388 // can't have more than 24 maxplayers; can't have more than max
389 if (srv->s_plrs > srv->s_maxplrs || srv->s_maxplrs > 24)
390 score += MS_MAXHEUR;
392 // name and map have to be non-garbage
393 if (!d_strisprint(srv->s_map) || !d_strisprint(srv->s_name))
394 score += MS_MAXHEUR;
396 // these protocols don't exist
397 if (srv->s_protocol < 100 || srv->s_protocol > 250)
398 score += MS_MAXHEUR;
400 // the game doesn't allow server names longer than 64 chars
401 if (strlen(srv->s_name) > 64)
402 score += MS_MAXHEUR;
404 // game mode has to actually exist
405 if (srv->s_mode > 5)
406 score += MS_MAXHEUR;
408 // password field can be either 0 or 1
409 if (srv->s_pw > 1)
410 score += MS_MAXHEUR;
412 // port has to be set, although the game allows you to set it to 0
413 // if (!srv->s_port)
414 // score += MS_MAXHEUR;
416 // servers usually don't update more often than once every 30 seconds
417 if (now - srv->lasttime < 5)
418 score += MS_MAXHEUR / 2;
420 return score;
424 void ban_add (const ENetAddress *addr, const char *reason) {
425 const time_t now = time(NULL);
427 ms_ban_record *rec = ban_record_add_addr(addr, 0, 0);
428 if (!rec) d_error(LC_MS_OOM, 1);
430 rec->cur_ban = now + ban_get_time(rec->ban_count);
431 rec->ban_count++;
433 printf(LC_MS_BANNED, rec->ip, d_strtime(rec->cur_ban), reason, rec->ban_count);
435 ban_save_list(MS_BAN_FILE);
439 void d_deinit(void) {
440 ban_save_list(MS_BAN_FILE);
444 int main (int argc, char *argv[]) {
445 d_getargs(argc, argv);
447 if (enet_initialize()) {
448 d_error("Could not init ENet!", 1);
449 return EXIT_FAILURE;
452 printf(LC_MS_INIT, ms_port);
454 d_readtextfile(MS_MOTD_FILE, ms_motd, sizeof(ms_motd));
455 d_readtextfile(MS_URGENT_FILE, ms_urgent, sizeof(ms_urgent));
456 ban_load_list(MS_BAN_FILE);
458 if (ms_motd[0]) printf(LC_MS_MOTD, ms_motd);
459 if (ms_urgent[0]) printf(LC_MS_URGENT, ms_urgent);
461 for (int i = 0; i < NET_MAXCLIENTS; ++i) ms_peers[i] = NULL;
463 for (int i = 0; i < MS_MAXSRVS; ++i) {
464 ms_srv[i].used = 0;
465 ms_srv[i].s_ip[0] = '\0';
466 ms_srv[i].s_name[0] = '\0';
467 ms_srv[i].s_map[0] = '\0';
468 ms_srv[i].deathtime = 0;
471 ENetAddress addr;
472 addr.host = ENET_HOST_ANY;
473 addr.port = ms_port;
475 ms_host = enet_host_create(&addr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
476 if (!ms_host) {
477 d_error("Could not create host on specified port!", 1);
478 return EXIT_FAILURE;
481 atexit(d_deinit);
483 ENetEvent event;
484 int shutdown = 0;
485 enet_uint8 msg = 255;
487 char ip[17];
488 enet_uint16 port = 0;
490 char *name = NULL;
491 char *map = NULL;
492 char *clientver = NULL;
493 enet_uint8 gm = 0;
494 enet_uint16 pl = 0;
495 enet_uint16 mpl = 0;
497 enet_uint8 proto = 0;
498 enet_uint8 pw = 0;
499 while (!shutdown) {
500 while (enet_host_service(ms_host, &event, 5000) > 0) {
501 if (event.peer && ban_check(&(event.peer->address)))
502 continue;
504 const time_t now = time(NULL);
506 switch (event.type) {
507 case ENET_EVENT_TYPE_CONNECT:
508 printf(LC_MS_CONN, event.peer->address.host, event.peer->address.port);
509 break;
511 case ENET_EVENT_TYPE_RECEIVE:
512 if (!event.peer) continue;
514 b_read = 0;
515 msg = b_read_uint8(event.packet->data, &b_read);
517 switch (msg) {
518 case NET_MSG_ADD:
519 enet_address_get_host_ip(&(event.peer->address), ip, 17);
520 port = b_read_uint16(event.packet->data, &b_read);
522 name = b_read_dstring(event.packet->data, &b_read);
523 map = b_read_dstring(event.packet->data, &b_read);
524 gm = b_read_uint8(event.packet->data, &b_read);
526 pl = b_read_uint8(event.packet->data, &b_read);
527 mpl = b_read_uint8(event.packet->data, &b_read);
529 proto = b_read_uint8(event.packet->data, &b_read);
530 pw = b_read_uint8(event.packet->data, &b_read);
532 for (int i = 0; i < MS_MAXSRVS; ++i) {
533 if (ms_srv[i].used) {
534 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
535 if (ban_heur(ms_srv + i, now) >= MS_MAXHEUR) {
536 ban_add(&(event.peer->address), LC_MS_BANHEUR);
537 break;
540 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
541 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
542 ms_srv[i].s_plrs = pl;
543 ms_srv[i].s_maxplrs = mpl;
544 ms_srv[i].s_pw = pw;
545 ms_srv[i].s_mode = gm;
547 ms_srv[i].deathtime = now + ms_timeout;
548 ms_srv[i].lasttime = now;
550 printf(LC_MS_UPD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
551 break;
553 } else {
554 strncpy(ms_srv[i].s_ip, ip, sizeof(ms_srv[i].s_ip));
555 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
556 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
557 ms_srv[i].s_port = port;
558 ms_srv[i].s_plrs = pl;
559 ms_srv[i].s_maxplrs = mpl;
560 ms_srv[i].s_pw = pw;
561 ms_srv[i].s_mode = gm;
562 ms_srv[i].s_protocol = proto;
563 ms_srv[i].deathtime = now + ms_timeout;
564 ms_srv[i].lasttime = now;
566 if (ban_heur(ms_srv + i, now) >= MS_MAXHEUR) {
567 ban_add(&(event.peer->address), LC_MS_BANHEUR);
568 break;
571 ms_srv[i].used = 1;
573 printf(LC_MS_ADD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
575 ++ms_count;
576 break;
579 free(name);
580 free(map);
581 break;
583 case NET_MSG_RM:
584 enet_address_get_host_ip(&(event.peer->address), ip, 17);
585 port = b_read_uint16(event.packet->data, &b_read);
586 for (int i = 0; i < MS_MAXSRVS; ++i) {
587 if (ms_srv[i].used) {
588 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
589 if (ban_heur(ms_srv + i, now) >= MS_MAXHEUR) {
590 ban_add(&(event.peer->address), LC_MS_BANHEUR);
591 break;
593 ms_srv[i].used = 0;
594 printf(LC_MS_RM, i, ip, port);
595 --ms_count;
599 break;
601 case NET_MSG_LIST:
602 b_write = 0;
603 b_write_uint8(b_send, &b_write, NET_MSG_LIST);
605 if (event.packet->dataLength > 2) {
606 // holy shit a fresh client
607 clientver = b_read_dstring(event.packet->data, &b_read);
608 b_write_uint8(b_send, &b_write, ms_count);
609 } else {
610 // old client, feed them bullshit first
611 b_write_uint8(b_send, &b_write, ms_count + 2);
612 for (int i = 0; i < MS_FAKESRVS; ++i)
613 b_write_server(b_send, &b_write, ms_fake_srv[i]);
616 for (int i = 0; i < MS_MAXSRVS; ++i) {
617 if (ms_srv[i].used) b_write_server(b_send, &b_write, ms_srv[i]);
620 if (clientver) {
621 // TODO: check if this client is outdated (?) and send back new verstring
622 // for now just write the same shit back
623 b_write_dstring(b_send, &b_write, clientver);
624 // write the motd and urgent message
625 b_write_dstring(b_send, &b_write, ms_motd);
626 b_write_dstring(b_send, &b_write, ms_urgent);
629 ENetPacket *p = enet_packet_create(b_send, b_write, ENET_PACKET_FLAG_RELIABLE);
630 enet_peer_send(event.peer, NET_CH_MAIN, p);
631 enet_host_flush(ms_host);
633 printf(LC_MS_LIST, event.peer->address.host, event.peer->address.port, clientver ? clientver : "<old>");
634 free(clientver);
635 clientver = NULL;
636 break;
639 enet_packet_destroy(event.packet);
640 break;
642 default:
643 break;
647 time_t now = time(NULL);
648 for (int i = 0; i < MS_MAXSRVS; ++i) {
649 if (ms_srv[i].used) {
650 if (ms_srv[i].deathtime <= now) {
651 ms_srv[i].used = 0;
652 printf(LC_MS_TIME, i, ms_srv[i].s_ip, ms_srv[i].s_port);
653 --ms_count;
659 printf(LC_MS_DIE);
661 return EXIT_SUCCESS;