DEADSOFTWARE

4298476423109870b8973e2e77d77d604cdaf8e3
[d2df-sdl.git] / src / mastersrv / master.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <stdint.h>
4 #include <stdbool.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <string.h>
8 #include <time.h>
9 #include <signal.h>
11 #include <enet/enet.h>
12 #include <enet/types.h>
14 #define MS_VERSION "0.3"
15 #define MS_MAX_SERVERS 128
16 #define MS_MAX_CLIENTS (MS_MAX_SERVERS + 1)
17 #define MS_URGENT_FILE "urgent.txt"
18 #define MS_MOTD_FILE "motd.txt"
19 #define MS_BAN_FILE "master_bans.txt"
21 #define DEFAULT_SPAM_CAP 10
22 #define DEFAULT_MAX_SERVERS MS_MAX_SERVERS
23 #define DEFAULT_MAX_PER_HOST 4
24 #define DEFAULT_TIMEOUT 100
25 #define DEFAULT_PORT 25665
27 #define NET_BUFSIZE 65536
28 #define NET_FULLMASK 0xFFFFFFFF
30 #define SV_PROTO_MIN 140
31 #define SV_PROTO_MAX 210
32 #define SV_NAME_MAX 64
33 #define SV_MAP_MAX 64
34 #define SV_MAX_PLAYERS 24
35 #define SV_MAX_GAMEMODE 5
36 #define SV_NEW_SERVER_INTERVAL 3
38 #define MAX_STRLEN 0xFF
40 enum log_severity_e {
41 LOG_NOTE,
42 LOG_WARN,
43 LOG_ERROR
44 };
46 enum net_ch_e {
47 NET_CH_MAIN,
48 NET_CH_UPD,
49 NET_CH_COUNT
50 };
52 enum net_msg_e {
53 NET_MSG_ADD = 200,
54 NET_MSG_RM = 201,
55 NET_MSG_LIST = 202
56 };
58 enum sv_flags_e {
59 SV_FL_PASSWORD = 1 << 0,
60 SV_FL_VERIFIED = 1 << 1,
61 SV_FL_MAX = SV_FL_PASSWORD | SV_FL_VERIFIED,
62 };
64 typedef struct enet_buf_s {
65 enet_uint8 *data;
66 size_t size;
67 size_t pos;
68 int overflow;
69 } enet_buf_t;
71 typedef struct ban_record_s {
72 enet_uint32 host;
73 enet_uint32 mask;
74 int ban_count;
75 time_t cur_ban;
76 struct ban_record_s *next;
77 struct ban_record_s *prev;
78 } ban_record_t;
80 typedef struct server_s {
81 enet_uint32 host; // BE; 0 means this slot is unused
82 enet_uint16 port; // LE, which is what the game and enet both expect
83 enet_uint8 flags;
84 enet_uint8 proto;
85 enet_uint8 gamemode;
86 enet_uint8 players;
87 enet_uint8 maxplayers;
88 char name[MAX_STRLEN + 2];
89 char map[MAX_STRLEN + 2];
90 time_t death_time;
91 time_t timestamp;
92 } server_t;
94 // real servers
95 static server_t servers[MS_MAX_SERVERS];
96 static int max_servers = DEFAULT_MAX_SERVERS;
97 static int max_servers_per_host = DEFAULT_MAX_PER_HOST;
98 static int num_servers = 0;
100 // fake servers to show on old versions of the game
101 static const server_t fake_servers[] = {
103 .name = "! \xc2\xc0\xd8\xc0 \xca\xce\xcf\xc8\xdf \xc8\xc3\xd0\xdb "
104 "\xd3\xd1\xd2\xc0\xd0\xc5\xcb\xc0! "
105 "\xd1\xca\xc0\xd7\xc0\xc9\xd2\xc5 \xcd\xce\xc2\xd3\xde C "
106 "doom2d.org !",
107 .map = "! Your game is outdated. "
108 "Get latest version at doom2d.org !",
109 .proto = 255,
110 },
112 .name = "! \xcf\xd0\xce\xc1\xd0\xce\xd1\xdcTE \xcf\xce\xd0\xd2\xdb "
113 "25666 \xc8 57133 HA CEPBEPE \xcf\xc5\xd0\xc5\xc4 \xc8\xc3\xd0\xce\xc9 !",
114 .map = "! Forward ports 25666 and 57133 before hosting !",
115 .proto = 255,
116 },
117 };
118 static const int num_fake_servers = sizeof(fake_servers) / sizeof(*fake_servers);
120 // ban list
121 static ban_record_t *banlist;
123 // settings
124 static int ms_port = DEFAULT_PORT;
125 static int ms_timeout = DEFAULT_TIMEOUT;
126 static int ms_spam_cap = DEFAULT_SPAM_CAP;
127 static char ms_motd[MAX_STRLEN + 1] = "";
128 static char ms_urgent[MAX_STRLEN + 1] = "";
129 static ENetHost *ms_host;
131 // network buffers
132 static enet_uint8 buf_send_data[NET_BUFSIZE];
133 static enet_buf_t buf_send = { .data = buf_send_data, .size = sizeof(buf_send_data) };
134 static enet_buf_t buf_recv; // rx data supplied by enet packets
136 // stupid client spam filter
137 static enet_uint32 cl_last_addr;
138 static time_t cl_last_time;
139 static int cl_spam_cnt;
141 /* common utility functions */
143 static char *u_vabuf(void) {
144 static char vabuf[4][MAX_STRLEN];
145 static int idx = 0;
146 char *ret = vabuf[idx++];
147 if (idx >= 4) idx = 0;
148 return ret;
151 static const char *u_strtime(const time_t t) {
152 char *buf = u_vabuf();
153 struct tm *ptm = localtime(&t);
154 strftime(buf, MAX_STRLEN - 1, "%d/%m/%y %H:%M:%S", ptm);
155 return buf;
158 static inline const char *u_logprefix(const enum log_severity_e s) {
159 switch (s) {
160 case LOG_WARN: return "WARNING: ";
161 case LOG_ERROR: return "ERROR: ";
162 default: return "";
166 static void u_log(const enum log_severity_e severity, const char *fmt, ...) {
167 printf("[%s] %s", u_strtime(time(NULL)), u_logprefix(severity));
168 va_list args;
169 va_start(args, fmt);
170 vprintf(fmt, args);
171 va_end(args);
172 printf("\n");
175 static void __attribute__((noreturn)) u_fatal(const char *fmt, ...) {
176 fprintf(stderr, "[%s] FATAL ERROR:\n", u_strtime(time(NULL)));
177 va_list args;
178 va_start(args, fmt);
179 vfprintf(stderr, fmt, args);
180 va_end(args);
181 fprintf(stderr, "\n");
182 fflush(stderr);
183 exit(1);
186 static bool u_strisprint(const char *str) {
187 if (!str || !*str)
188 return false;
189 for (const char *p = str; *p; ++p) {
190 // only stuff before space, DEL, NBSP and SHY are considered garbage since we're on 1251
191 if (*p < 0x20 || *p == 0x7F || *p == 0xA0 || *p == 0xAD)
192 return false;
194 return true;
197 static bool u_strisver(const char *str) {
198 if (!str || !*str)
199 return false;
200 for (const char *p = str; *p; ++p) {
201 // version strings consist of 0-9 . and space
202 if (!isdigit(*p) && *p != '.' && *p != ' ')
203 return false;
205 return true;
208 static const char *u_iptostr(const enet_uint32 host) {
209 ENetAddress addr = { .host = host, .port = 0 };
210 char *buf = u_vabuf();
211 enet_address_get_host_ip(&addr, buf, MAX_STRLEN - 1);
212 return buf;
215 static bool u_readtextfile(const char *fname, char *buf, size_t max) {
216 FILE *f = fopen(fname, "r");
217 char *const end = buf + max - 1;
218 char *p = buf;
219 if (f) {
220 char ln[max];
221 char *const lend = ln + max - 1;
222 while (p < end && fgets(ln, max, f)) {
223 for (char *n = ln; n < lend && *n && *n != '\r' && *n != '\n'; ++n) {
224 *(p++) = *n;
225 if (p == end) break;
228 *p = '\0';
229 fclose(f);
230 return true;
232 return false;
235 static inline enet_uint32 u_prefixtomask(const enet_uint32 prefix) {
236 return ENET_HOST_TO_NET_32((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF);
239 static inline enet_uint32 u_masktoprefix(const enet_uint32 mask) {
240 return (32 - __builtin_ctz(mask));
243 static inline void u_printsv(const server_t *sv) {
244 printf("* addr: %s:%d\n", u_iptostr(sv->host), sv->port);
245 printf("* name: %s\n", sv->name);
246 printf("* map: %s (mode %d)\n", sv->map, sv->gamemode);
247 printf("* plrs: %d/%d\n", sv->players, sv->maxplayers);
248 printf("* flag: %04x\n", sv->flags);
251 /* buffer utility functions */
253 static inline int b_enough_left(enet_buf_t *buf, size_t size) {
254 if (buf->pos + size > buf->size) {
255 buf->overflow = 1;
256 return 0;
258 return 1;
261 static enet_uint8 b_read_uint8(enet_buf_t *buf) {
262 if (b_enough_left(buf, 1))
263 return buf->data[buf->pos++];
264 return 0;
267 static enet_uint16 b_read_uint16(enet_buf_t *buf) {
268 enet_uint16 ret = 0;
270 if (b_enough_left(buf, sizeof(ret))) {
271 ret = *(enet_uint16*)(buf->data + buf->pos);
272 buf->pos += sizeof(ret);
275 return ret;
278 static char *b_read_dstring(enet_buf_t *buf) {
279 char *ret = NULL;
281 if (b_enough_left(buf, 1)) {
282 const size_t len = b_read_uint8(buf);
283 if (b_enough_left(buf, len)) {
284 ret = malloc(len + 1);
285 memmove(ret, (char*)(buf->data + buf->pos), len);
286 buf->pos += len;
287 ret[len] = '\0';
291 return ret;
294 static char *b_read_dstring_to(enet_buf_t *buf, char *out, size_t out_size) {
295 if (b_enough_left(buf, 1)) {
296 const size_t len = b_read_uint8(buf);
297 if (b_enough_left(buf, len)) {
298 if (len < out_size) {
299 memmove(out, (char*)(buf->data + buf->pos), len);
300 out[len] = '\0';
301 } else if (out_size) {
302 out[0] = '\0';
304 buf->pos += len;
305 return out;
308 return NULL;
311 static void b_write_uint8(enet_buf_t *buf, enet_uint8 val) {
312 buf->data[buf->pos++] = val;
315 static void b_write_uint16(enet_buf_t *buf, enet_uint16 val) {
316 *(enet_uint16*)(buf->data + buf->pos) = val;
317 buf->pos += sizeof(val);
320 static void b_write_dstring(enet_buf_t *buf, const char* val) {
321 enet_uint8 len = strlen(val);
322 b_write_uint8(buf, len);
323 memmove((char*)(buf->data + buf->pos), val, len);
324 buf->pos += len;
327 void b_write_server(enet_buf_t *buf, const server_t *s) {
328 b_write_dstring(buf, u_iptostr(s->host));
329 b_write_uint16 (buf, s->port);
330 b_write_dstring(buf, s->name);
331 b_write_dstring(buf, s->map);
332 b_write_uint8 (buf, s->gamemode);
333 b_write_uint8 (buf, s->players);
334 b_write_uint8 (buf, s->maxplayers);
335 b_write_uint8 (buf, s->proto);
336 b_write_uint8 (buf, (s->flags & SV_FL_PASSWORD));
339 /* server functions */
341 static void sv_remove(const enet_uint32 host, const enet_uint16 port) {
342 for (int i = 0; i < max_servers; ++i) {
343 if (servers[i].host == host && servers[i].port == port) {
344 servers[i].host = 0;
345 servers[i].port = 0;
346 --num_servers;
351 static void sv_remove_by_host(enet_uint32 host, enet_uint32 mask) {
352 host &= mask;
353 for (int i = 0; i < max_servers; ++i) {
354 if (servers[i].host && (servers[i].host & mask) == host) {
355 servers[i].host = 0;
356 servers[i].port = 0;
357 --num_servers;
362 static int sv_count_by_host(enet_uint32 host, enet_uint32 mask) {
363 host &= mask;
364 int count = 0;
365 for (int i = 0; i < max_servers; ++i) {
366 if (servers[i].host && (servers[i].host & mask) == host)
367 ++count;
369 return count;
372 static time_t sv_last_timestamp_for_host(enet_uint32 host, enet_uint32 mask) {
373 host &= mask;
374 time_t last = 0;
375 for (int i = 0; i < max_servers; ++i) {
376 if (servers[i].host && (servers[i].host & mask) == host) {
377 if (servers[i].timestamp > last)
378 last = servers[i].timestamp;
381 return last;
384 static inline server_t *sv_find_or_add(const enet_uint32 host, const enet_uint32 port) {
385 server_t *empty = NULL;
386 for (int i = 0; i < max_servers; ++i) {
387 server_t *s = servers + i;
388 if (s->host == host && s->port == port)
389 return s; // this server already exists
390 if (!s->host && !empty)
391 empty = s; // remember the first empty slot in case it's needed later
393 return empty;
396 /* ban list functions */
398 static inline time_t ban_get_time(const int cnt) {
399 static const time_t times[] = {
400 1 * 5 * 60,
401 1 * 30 * 60,
402 1 * 60 * 60,
403 24 * 60 * 60,
404 72 * 60 * 60,
405 720 * 60 * 60,
406 8760 * 60 * 60,
407 };
409 static const size_t numtimes = sizeof(times) / sizeof(*times);
411 if (cnt >= numtimes || cnt < 0)
412 return times[numtimes - 1];
414 return times[cnt];
417 static ban_record_t *ban_check(const enet_uint32 host) {
418 const time_t now = time(NULL);
420 for (ban_record_t *b = banlist; b; b = b->next) {
421 if ((b->host & b->mask) == (host & b->mask)) {
422 if (b->cur_ban > now)
423 return b;
427 return NULL;
430 static inline ban_record_t *ban_record_check(const enet_uint32 host) {
431 for (ban_record_t *b = banlist; b; b = b->next) {
432 if ((b->host & b->mask) == (host & b->mask))
433 return b;
435 return NULL;
438 static ban_record_t *ban_record_add_addr(const enet_uint32 host, const enet_uint32 mask, const int cnt, const time_t cur) {
439 ban_record_t *rec = ban_record_check(host);
440 if (rec) return rec;
442 rec = calloc(1, sizeof(*rec));
443 if (!rec) return NULL;
445 rec->host = host & mask;
446 rec->mask = mask;
447 if (rec->mask == 0) rec->mask = NET_FULLMASK;
448 rec->ban_count = cnt;
449 rec->cur_ban = cur;
451 if (banlist) banlist->prev = rec;
452 rec->next = banlist;
453 banlist = rec;
455 return rec;
458 static ban_record_t *ban_record_add_ip(const char *ip, const int cnt, const time_t cur) {
459 enet_uint32 prefix = 32;
461 // find and get the prefix length, if any
462 char ip_copy[24] = { 0 };
463 strncpy(ip_copy, ip, sizeof(ip_copy) - 1);
464 char *slash = strrchr(ip_copy, '/');
465 if (slash) {
466 *slash++ = '\0'; // strip the prefix length off
467 if (*slash) prefix = atoi(slash);
470 ENetAddress addr = { 0 };
471 if (enet_address_set_host_ip(&addr, ip_copy) != 0) {
472 u_log(LOG_ERROR, "banlist: `%s` is not a valid IP address", ip_copy);
473 return NULL;
476 // transform prefix length into mask
477 const enet_uint32 mask = u_prefixtomask(prefix);
479 return ban_record_add_addr(addr.host, mask, cnt, cur);
482 static void ban_free_list(void) {
483 ban_record_t *rec = banlist;
484 while (rec) {
485 ban_record_t *next = rec->next;
486 free(rec);
487 rec = next;
489 banlist = NULL;
492 static void ban_load_list(const char *fname) {
493 FILE *f = fopen(fname, "r");
494 if (!f) {
495 u_log(LOG_WARN, "banlist: could not open %s for reading", fname);
496 return;
499 char ln[MAX_STRLEN] = { 0 };
501 while (fgets(ln, sizeof(ln), f)) {
502 for (int i = sizeof(ln) - 1; i >= 0; --i)
503 if (ln[i] == '\n' || ln[i] == '\r')
504 ln[i] = 0;
506 if (ln[0] == 0)
507 continue;
509 char ip[21] = { 0 }; // optionally includes the "/nn" prefix length at the end
510 time_t exp = 0;
511 int count = 0;
512 if (sscanf(ln, "%20s %ld %d", ip, &exp, &count) < 3) {
513 u_log(LOG_ERROR, "banlist: malformed line: `%s`", ln);
514 continue;
517 if (ban_record_add_ip(ip, count, exp))
518 u_log(LOG_NOTE, "banlist: banned %s until %s (ban level %d)", ip, u_strtime(exp), count);
521 fclose(f);
524 static void ban_save_list(const char *fname) {
525 FILE *f = fopen(fname, "w");
526 if (!f) {
527 u_log(LOG_ERROR, "banlist: could not open %s for writing", fname);
528 return;
531 for (ban_record_t *rec = banlist; rec; rec = rec->next) {
532 if (rec->ban_count)
533 fprintf(f, "%s/%u %ld %d\n", u_iptostr(rec->host), u_masktoprefix(rec->mask), rec->cur_ban, rec->ban_count);
536 fclose(f);
539 static bool ban_sanity_check(const server_t *srv) {
540 // can't have more than 24 maxplayers; can't have more than max
541 if (srv->players > srv->maxplayers || srv->maxplayers > SV_MAX_PLAYERS || srv->maxplayers == 0)
542 return false;
543 // name and map have to be non-garbage
544 if (!u_strisprint(srv->map) || !u_strisprint(srv->name))
545 return false;
546 // these protocols don't exist
547 if (srv->proto < SV_PROTO_MIN || srv->proto > SV_PROTO_MAX)
548 return false;
549 // the game doesn't allow server names longer than 64 chars
550 if (strlen(srv->name) > SV_NAME_MAX)
551 return false;
552 // game mode has to actually exist
553 if (srv->gamemode > SV_MAX_GAMEMODE)
554 return false;
555 // flags field can't be higher than the sum of all the flags
556 if (srv->flags > SV_FL_MAX)
557 return false;
558 return true;
561 static void ban_add(const enet_uint32 host, const char *reason) {
562 const time_t now = time(NULL);
564 ban_record_t *rec = ban_record_add_addr(host, NET_FULLMASK, 0, 0);
565 if (!rec) u_fatal("OOM trying to ban %s", u_iptostr(host));
567 rec->cur_ban = now + ban_get_time(rec->ban_count);
568 rec->ban_count++;
570 u_log(LOG_NOTE, "banned %s until %s, reason: %s, ban level: %d", u_iptostr(rec->host), u_strtime(rec->cur_ban), reason, rec->ban_count);
572 ban_save_list(MS_BAN_FILE);
574 sv_remove_by_host(host, NET_FULLMASK);
576 if (host == cl_last_addr)
577 cl_last_addr = 0;
580 static inline void ban_peer(ENetPeer *peer, const char *reason) {
581 if (peer) {
582 ban_add(peer->address.host, reason);
583 enet_peer_reset(peer);
587 /* main */
589 static void deinit(void) {
590 // ban_save_list(MS_BAN_FILE);
591 ban_free_list();
592 if (ms_host) {
593 enet_host_destroy(ms_host);
594 ms_host = NULL;
596 enet_deinitialize();
599 #ifdef SIGUSR1
600 static void sigusr_handler(int signum) {
601 if (signum == SIGUSR1) {
602 u_log(LOG_WARN, "received SIGUSR1, reloading banlist");
603 ban_free_list();
604 ban_load_list(MS_BAN_FILE);
607 #endif
609 static bool handle_msg(const enet_uint8 msgid, ENetPeer *peer) {
610 server_t *sv = NULL;
611 server_t tmpsv = { 0 };
612 char clientver[MAX_STRLEN] = { 0 };
613 const time_t now = time(NULL);
615 switch (msgid) {
616 case NET_MSG_ADD:
617 tmpsv.port = b_read_uint16(&buf_recv);
618 b_read_dstring_to(&buf_recv, tmpsv.name, sizeof(tmpsv.name));
619 b_read_dstring_to(&buf_recv, tmpsv.map, sizeof(tmpsv.map));
620 tmpsv.gamemode = b_read_uint8(&buf_recv);
621 tmpsv.players = b_read_uint8(&buf_recv);
622 tmpsv.maxplayers = b_read_uint8(&buf_recv);
623 tmpsv.proto = b_read_uint8(&buf_recv);
624 tmpsv.flags = b_read_uint8(&buf_recv);
626 if (buf_recv.overflow) {
627 ban_peer(peer, "malformed MSG_ADD");
628 return true;
631 sv = sv_find_or_add(peer->address.host, tmpsv.port);
632 if (!sv) {
633 u_log(LOG_ERROR, "ran out of server slots trying to add %s:%d", u_iptostr(peer->address.host), tmpsv.port);
634 return true;
637 if (sv->host == peer->address.host) {
638 // old server; update it
639 memcpy(sv->map, tmpsv.map, sizeof(sv->map));
640 memcpy(sv->name, tmpsv.name, sizeof(sv->name));
641 sv->players = tmpsv.players;
642 sv->maxplayers = tmpsv.maxplayers;
643 sv->flags = tmpsv.flags;
644 sv->gamemode = tmpsv.gamemode;
645 // first check if the new values are garbage
646 if (!ban_sanity_check(sv)) {
647 ban_peer(peer, "tripped sanity check");
648 return true;
650 // only then update the times
651 sv->death_time = now + ms_timeout;
652 sv->timestamp = now;
653 u_log(LOG_NOTE, "updated server #%d:", sv - servers);
654 u_printsv(sv);
655 } else {
656 // new server; first check if this host is creating too many servers in the list
657 if (max_servers_per_host) {
658 const int count = sv_count_by_host(peer->address.host, NET_FULLMASK);
659 if (count >= max_servers_per_host) {
660 ban_peer(peer, "too many servers in list");
661 return true;
663 /*
664 // FIXME: commented out as this might trip when the master restarts
665 if (count > 0) {
666 // check if this is too soon to create a new server
667 const time_t delta = now - sv_last_timestamp_for_host(peer->address.host, NET_FULLMASK);
668 if (delta < count * SV_NEW_SERVER_INTERVAL) {
669 ban_peer(peer, "creating servers too fast");
670 return true;
673 */
675 // then add that shit
676 *sv = tmpsv;
677 sv->host = peer->address.host;
678 sv->death_time = now + ms_timeout;
679 sv->timestamp = now;
680 if (!ban_sanity_check(sv)) {
681 sv->host = 0;
682 sv->port = 0;
683 ban_peer(peer, "tripped sanity check");
684 return true;
686 ++num_servers;
687 u_log(LOG_NOTE, "added new server #%d:", sv - servers);
688 u_printsv(sv);
690 return true;
692 case NET_MSG_RM:
693 tmpsv.port = b_read_uint16(&buf_recv);
694 if (buf_recv.overflow) {
695 ban_peer(peer, "malformed MSG_RM");
696 return true;
698 sv_remove(peer->address.host, tmpsv.port);
699 return true;
701 case NET_MSG_LIST:
702 buf_send.pos = 0;
703 b_write_uint8(&buf_send, NET_MSG_LIST);
705 clientver[0] = 0;
706 if (buf_recv.size > 2) {
707 // holy shit a fresh client
708 b_read_dstring_to(&buf_recv, clientver, sizeof(clientver));
709 b_write_uint8(&buf_send, num_servers);
710 } else {
711 // old client; feed him fake servers first
712 b_write_uint8(&buf_send, num_servers + num_fake_servers);
713 for (int i = 0; i < num_fake_servers; ++i)
714 b_write_server(&buf_send, &fake_servers[i]);
717 if (buf_recv.overflow) {
718 ban_peer(peer, "malformed MSG_LIST");
719 return true;
722 if (clientver[0] && !u_strisver(clientver)) {
723 ban_peer(peer, "malformed MSG_LIST clientver");
724 return true;
727 for (int i = 0; i < max_servers; ++i) {
728 if (servers[i].host)
729 b_write_server(&buf_send, servers + i);
732 if (clientver[0]) {
733 // TODO: check if this client is outdated (?) and send back new verstring
734 // for now just write the same shit back
735 b_write_dstring(&buf_send, clientver);
736 // write the motd and urgent message
737 b_write_dstring(&buf_send, ms_motd);
738 b_write_dstring(&buf_send, ms_urgent);
741 ENetPacket *p = enet_packet_create(buf_send.data, buf_send.pos, ENET_PACKET_FLAG_RELIABLE);
742 enet_peer_send(peer, NET_CH_MAIN, p);
743 enet_host_flush(ms_host);
745 u_log(LOG_NOTE, "sent server list to %s:%d (ver %s)", u_iptostr(peer->address.host), peer->address.port, clientver[0] ? clientver : "<old>");
746 return true;
748 default:
749 break;
752 return false;
755 static void print_usage(void) {
756 printf("Usage: d2df_master [OPTIONS...]\n");
757 printf("Available options:\n");
758 printf("-h show this message and exit\n");
759 printf("-p N listen on port N (default: %d)\n", DEFAULT_PORT);
760 printf("-t N seconds before server is removed from list (default: %d)\n", DEFAULT_TIMEOUT);
761 printf("-s N max number of servers in server list, 1-%d (default: %d)\n", MS_MAX_SERVERS, DEFAULT_MAX_SERVERS);
762 printf("-d N if N > 0, disallow more than N servers on the same IP (default: %d)\n", DEFAULT_MAX_PER_HOST);
763 printf("-f N crappy spam filter: ban people after they send N requests in a row too fast (default: %d)\n", DEFAULT_SPAM_CAP);
764 fflush(stdout);
767 static inline bool parse_int_arg(int argc, char **argv, const int i, const char *name, int vmin, int vmax, int *outval) {
768 if (strcmp(name, argv[i]))
769 return false;
771 if (i >= argc - 1) {
772 fprintf(stderr, "expected integer value after %s\n", name);
773 return false;
776 const int v = atoi(argv[i + 1]);
777 if (v < vmin || v > vmax) {
778 fprintf(stderr, "expected integer value in range %d - %d\n", vmin, vmax);
779 return false;
782 *outval = v;
783 return true;
786 static bool parse_args(int argc, char **argv) {
787 if (argc < 2)
788 return true;
790 if (!strcmp(argv[1], "-h")) {
791 print_usage();
792 return false;
795 for (int i = 1; i < argc; ++i) {
796 const bool success =
797 parse_int_arg(argc, argv, i, "-p", 1, 0xFFFF, &ms_port)
798 || parse_int_arg(argc, argv, i, "-t", 1, 0x7FFFFFFF, &ms_timeout)
799 || parse_int_arg(argc, argv, i, "-s", 1, MS_MAX_SERVERS, &max_servers)
800 || parse_int_arg(argc, argv, i, "-d", 0, MS_MAX_SERVERS, &max_servers_per_host)
801 || parse_int_arg(argc, argv, i, "-f", 0, 0xFFFF, &ms_spam_cap);
802 if (success) {
803 ++i;
804 } else {
805 fprintf(stderr, "unknown or invalid argument: %s\n", argv[i]);
806 return false;
810 return true;
813 // a stupid thing to filter sustained spam from a single IP
814 static bool spam_filter(ENetPeer *peer) {
815 const time_t now = time(NULL);
816 if (peer->address.host == cl_last_addr) {
817 // spam === sending shit faster than once a second
818 if (now - cl_last_time < 1) {
819 if (cl_spam_cnt > 1)
820 u_log(LOG_WARN, "address %s is sending packets too fast", u_iptostr(peer->address.host));
821 if (++cl_spam_cnt >= ms_spam_cap) {
822 ban_peer(peer, "spam");
823 return true;
825 } else {
826 cl_spam_cnt = 0;
828 } else {
829 cl_last_addr = peer->address.host;
830 cl_spam_cnt = 0;
832 cl_last_time = now;
833 return false;
836 int main(int argc, char **argv) {
837 if (enet_initialize() != 0)
838 u_fatal("could not init enet");
840 if (!parse_args(argc, argv))
841 return 1; // early exit
843 u_log(LOG_NOTE, "d2df master server starting on port %d", ms_port);
845 if (!u_readtextfile(MS_MOTD_FILE, ms_motd, sizeof(ms_motd)))
846 u_log(LOG_NOTE, "couldn't read motd from %s", MS_MOTD_FILE);
847 else
848 u_log(LOG_NOTE, "motd: %s", ms_motd);
850 if (!u_readtextfile(MS_URGENT_FILE, ms_urgent, sizeof(ms_urgent)))
851 u_log(LOG_NOTE, "couldn't read urgentmsg from %s", MS_URGENT_FILE);
852 else
853 u_log(LOG_NOTE, "urgentmsg: %s", ms_urgent);
855 ban_load_list(MS_BAN_FILE);
857 atexit(deinit);
859 #ifdef SIGUSR1
860 signal(SIGUSR1, sigusr_handler);
861 #endif
863 ENetAddress addr;
864 addr.host = 0;
865 addr.port = ms_port;
866 ms_host = enet_host_create(&addr, MS_MAX_CLIENTS, NET_CH_COUNT, 0, 0);
867 if (!ms_host)
868 u_fatal("could not create enet host on port %d", ms_port);
870 bool running = true;
871 enet_uint8 msgid = 0;
872 ENetEvent event;
873 while (running) {
874 while (enet_host_service(ms_host, &event, 5000) > 0) {
875 bool filtered = !event.peer || ban_check(event.peer->address.host);
876 if (!filtered && event.type != ENET_EVENT_TYPE_DISCONNECT)
877 filtered = spam_filter(event.peer);
879 if (!filtered) {
880 switch (event.type) {
881 case ENET_EVENT_TYPE_CONNECT:
882 u_log(LOG_NOTE, "%s:%d connected", u_iptostr(event.peer->address.host), event.peer->address.port);
883 break;
885 case ENET_EVENT_TYPE_RECEIVE:
886 if (!event.packet || event.packet->dataLength == 0) {
887 ban_peer(event.peer, "empty packet");
888 break;
890 // set up receive buffer
891 buf_recv.pos = 0;
892 buf_recv.overflow = 0;
893 buf_recv.data = event.packet->data;
894 buf_recv.size = event.packet->dataLength;
895 // read message id and handle the message
896 msgid = b_read_uint8(&buf_recv);
897 if (!handle_msg(msgid, event.peer)) {
898 // cheeky cunt sending invalid messages
899 ban_peer(event.peer, "unknown message");
901 break;
903 default:
904 break;
906 } else if (event.peer) {
907 enet_peer_reset(event.peer);
910 if (event.packet) {
911 buf_recv.data = NULL;
912 buf_recv.pos = 0;
913 buf_recv.size = 0;
914 buf_recv.overflow = 0;
915 enet_packet_destroy(event.packet);
919 const time_t now = time(NULL);
920 for (int i = 0; i < max_servers; ++i) {
921 if (servers[i].host) {
922 if (servers[i].death_time <= now) {
923 u_log(LOG_NOTE, "server #%d %s:%d timed out", i, u_iptostr(servers[i].host), servers[i].port);
924 servers[i].host = 0;
925 servers[i].port = 0;
926 --num_servers;