X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fmastersrv%2Fmaster.c;h=c68d59c1042f9b090632971b5e696648df28ec3e;hb=e1664ed8cea6e3970b892b9b50b60a1fb0c4b017;hp=6573cc62337cd76c634b40626bf940ee069b2775;hpb=45395d68172f014b5eb33677db89e47b5555ed0d;p=d2df-sdl.git diff --git a/src/mastersrv/master.c b/src/mastersrv/master.c index 6573cc6..c68d59c 100644 --- a/src/mastersrv/master.c +++ b/src/mastersrv/master.c @@ -1,3 +1,18 @@ +/* Copyright (C) Doom 2D: Forever Developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License ONLY. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include #include #include @@ -7,8 +22,11 @@ #include #include #include +#include +#include +#include +#include -#define ENET_DEBUG 1 #include #include @@ -18,6 +36,7 @@ #define MS_URGENT_FILE "urgent.txt" #define MS_MOTD_FILE "motd.txt" #define MS_BAN_FILE "master_bans.txt" +#define MS_PIPE_FILE "d2df_master.pipe" #define DEFAULT_SPAM_CAP 10 #define DEFAULT_MAX_SERVERS MS_MAX_SERVERS @@ -146,6 +165,17 @@ static int cl_spam_cnt; /* common utility functions */ +static char *u_strstrip(char *p) { + if (!p) return p; + while (isspace(*p)) ++p; + const size_t len = strlen(p); + if (len) { + for (size_t i = len - 1; i && isspace(p[i]); --i) + p[i] = '\0'; + } + return p; +} + static char *u_vabuf(void) { static char vabuf[4][MAX_STRLEN]; static int idx = 0; @@ -243,7 +273,7 @@ static inline enet_uint32 u_prefixtomask(const enet_uint32 prefix) { } static inline enet_uint32 u_masktoprefix(const enet_uint32 mask) { - return (32 - __builtin_ctz(mask)); + return (32 - __builtin_ctz(ENET_NET_TO_HOST_32(mask))); } static inline void u_printsv(const server_t *sv) { @@ -349,11 +379,11 @@ static inline void sv_remove(server_t *sv) { // drop the associated peer, if any if (sv->peer && sv->peer->state == ENET_PEER_STATE_CONNECTED && sv->peer->data == sv) { sv->peer->data = NULL; - sv->peer = NULL; enet_peer_reset(sv->peer); } sv->host = 0; sv->port = 0; + sv->peer = NULL; --num_servers; } } @@ -407,6 +437,14 @@ static inline server_t *sv_find_or_add(const enet_uint32 host, const enet_uint32 return empty; } +static inline void sv_clear_peer(ENetPeer *peer) { + server_t *sv = peer->data; + if (sv) { + sv->peer = NULL; + peer->data = NULL; + } +} + /* ban list functions */ static inline time_t ban_get_time(const int cnt) { @@ -469,7 +507,7 @@ static ban_record_t *ban_record_add_addr(const enet_uint32 host, const enet_uint return rec; } -static ban_record_t *ban_record_add_ip(const char *ip, const int cnt, const time_t cur) { +static enet_uint32 ban_parse_ip_mask(const char *ip, enet_uint32 *out_mask) { enet_uint32 prefix = 32; // find and get the prefix length, if any @@ -482,15 +520,23 @@ static ban_record_t *ban_record_add_ip(const char *ip, const int cnt, const time } ENetAddress addr = { 0 }; - if (enet_address_set_host_ip(&addr, ip_copy) != 0) { - u_log(LOG_ERROR, "banlist: `%s` is not a valid IP address", ip_copy); - return NULL; - } + if (enet_address_set_host_ip(&addr, ip_copy) != 0) + return 0; // transform prefix length into mask - const enet_uint32 mask = u_prefixtomask(prefix); + *out_mask = u_prefixtomask(prefix); + + return addr.host; +} - return ban_record_add_addr(addr.host, mask, cnt, cur); +static ban_record_t *ban_record_add_ip(const char *ip, const int cnt, const time_t cur) { + enet_uint32 mask = 0; + const enet_uint32 host = ban_parse_ip_mask(ip, &mask); + if (!host) { + u_log(LOG_ERROR, "banlist: `%s` is not a valid address", ip); + return NULL; + } + return ban_record_add_addr(host, mask, cnt, cur); } static void ban_free_list(void) { @@ -503,6 +549,30 @@ static void ban_free_list(void) { banlist = NULL; } +static bool ban_record_remove_addr(const enet_uint32 host, const enet_uint32 mask) { + for (ban_record_t *b = banlist; b; b = b->next) { + if ((b->host == host) && (b->mask == mask)) { + if (b == banlist) + banlist = b->next; + if (b->next) b->next->prev = b->prev; + if (b->prev) b->prev->next = b->next; + free(b); + return true; + } + } + return false; +} + +static bool ban_record_remove_ip(const char *ip) { + enet_uint32 mask = 0; + const enet_uint32 host = ban_parse_ip_mask(ip, &mask); + if (!host) { + u_log(LOG_ERROR, "unban: `%s` is not a valid address", ip); + return NULL; + } + return ban_record_remove_addr(host, mask); +} + static void ban_load_list(const char *fname) { FILE *f = fopen(fname, "r"); if (!f) { @@ -521,13 +591,14 @@ static void ban_load_list(const char *fname) { continue; char ip[21] = { 0 }; // optionally includes the "/nn" prefix length at the end - time_t exp = 0; + int expd = 0; int count = 0; - if (sscanf(ln, "%20s %ld %d", ip, &exp, &count) < 3) { + if (sscanf(ln, "%20s %d %d", ip, &expd, &count) < 3) { u_log(LOG_ERROR, "banlist: malformed line: `%s`", ln); continue; } + const time_t exp = (time_t)expd; // shut up gcc if (ban_record_add_ip(ip, count, exp)) u_log(LOG_NOTE, "banlist: banned %s until %s (ban level %d)", ip, u_strtime(exp), count); } @@ -544,7 +615,7 @@ static void ban_save_list(const char *fname) { for (ban_record_t *rec = banlist; rec; rec = rec->next) { if (rec->ban_count) - fprintf(f, "%s/%u %ld %d\n", u_iptostr(rec->host), u_masktoprefix(rec->mask), rec->cur_ban, rec->ban_count); + fprintf(f, "%s/%u %d %d\n", u_iptostr(rec->host), u_masktoprefix(rec->mask), (int)rec->cur_ban, rec->ban_count); } fclose(f); @@ -572,6 +643,25 @@ static bool ban_sanity_check(const server_t *srv) { return true; } +static void ban_add_mask(const enet_uint32 host, const enet_uint32 mask, const char *reason) { + const time_t now = time(NULL); + + ban_record_t *rec = ban_record_add_addr(host, mask, 0, 0); + if (!rec) u_fatal("OOM trying to ban %s", u_iptostr(host)); + + rec->cur_ban = now + ban_get_time(rec->ban_count); + rec->ban_count++; + + 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); + + ban_save_list(MS_BAN_FILE); + + sv_remove_by_host(host, mask); + + if (host == cl_last_addr) + cl_last_addr = 0; +} + static void ban_add(const enet_uint32 host, const char *reason) { const time_t now = time(NULL); @@ -591,16 +681,94 @@ static void ban_add(const enet_uint32 host, const char *reason) { cl_last_addr = 0; } +static void ban_remove_mask(const enet_uint32 host, const enet_uint32 mask) { + if (!ban_record_remove_addr(host, mask)) { + u_log(LOG_ERROR, "could not find %s in ban list", u_iptostr(host)); + return; + } + u_log(LOG_NOTE, "unbanned %s", u_iptostr(host)); + ban_save_list(MS_BAN_FILE); +} + static inline void ban_peer(ENetPeer *peer, const char *reason) { if (peer) { ban_add(peer->address.host, reason); - peer->data = NULL; + sv_clear_peer(peer); enet_peer_reset(peer); } } /* main */ +#if ENABLE_PIPE + +static int io_fd = -1; + +static bool io_install_pipe(void) { + const int rc = mkfifo(MS_PIPE_FILE, 0664); + if (rc < 0 && errno != EEXIST) { + u_log(LOG_ERROR, "io_install_pipe(): mkfifo(): %s", strerror(errno)); + return false; + } + + io_fd = open(MS_PIPE_FILE, O_RDONLY | O_NONBLOCK); + if (io_fd < 0) { + u_log(LOG_ERROR, "io_install_pipe(): open(): %s", strerror(errno)); + remove(MS_PIPE_FILE); + return false; + } + + return true; +} + +static void io_uninstall_pipe(void) { + if (io_fd >= 0) { + close(io_fd); + io_fd = -1; + } + remove(MS_PIPE_FILE); +} + +static void io_read_commands(void) { + if (io_fd < 0) + return; + + char cmd[128]; + const int cmd_len = read(io_fd, cmd, sizeof(cmd) - 1); + if (cmd_len < 1) + return; + cmd[cmd_len] = '\0'; + + if (!strncmp(cmd, "ban ", 4)) { + const char *ip = u_strstrip(cmd + 4); // skip "ban " + enet_uint32 mask = 0; + enet_uint32 host = ban_parse_ip_mask(ip, &mask); + if (!host) { + u_log(LOG_ERROR, "ban: `%s` is not a valid address", ip); + return; + } + ban_add_mask(host, mask, "banned by console"); + } else if (!strncmp(cmd, "unban ", 6)) { + const char *ip = u_strstrip(cmd + 6); // skip "unban " + enet_uint32 mask = 0; + enet_uint32 host = ban_parse_ip_mask(ip, &mask); + if (!host) { + u_log(LOG_ERROR, "ban: `%s` is not a valid address", ip); + return; + } + ban_remove_mask(host, mask); + } else if (!strncmp(cmd, "reload", 6)) { + u_log(LOG_WARN, "reloading banlist"); + ban_free_list(); + ban_load_list(MS_BAN_FILE); + } else if (!strncmp(cmd, "die", 3)) { + u_log(LOG_WARN, "shutting down"); + exit(0); + } +} + +#endif + static void deinit(void) { // ban_save_list(MS_BAN_FILE); ban_free_list(); @@ -609,17 +777,10 @@ static void deinit(void) { ms_host = NULL; } enet_deinitialize(); -} - -#ifdef SIGUSR1 -static void sigusr_handler(int signum) { - if (signum == SIGUSR1) { - u_log(LOG_WARN, "received SIGUSR1, reloading banlist"); - ban_free_list(); - ban_load_list(MS_BAN_FILE); - } -} +#ifdef ENABLE_PIPE + io_uninstall_pipe(); #endif +} static bool handle_msg(const enet_uint8 msgid, ENetPeer *peer) { server_t *sv = NULL; @@ -668,9 +829,12 @@ static bool handle_msg(const enet_uint8 msgid, ENetPeer *peer) { // check if we're updating from a new peer if (sv->peer != peer) { // if there was an old one, kill it - if (sv->peer) - enet_peer_reset(peer); + if (sv->peer) { + sv->peer->data = NULL; + enet_peer_reset(sv->peer); + } sv->peer = peer; + peer->data = sv; } u_log(LOG_NOTE, "updated server #%d:", sv - servers); u_printsv(sv); @@ -721,7 +885,7 @@ static bool handle_msg(const enet_uint8 msgid, ENetPeer *peer) { } sv_remove_by_addr(peer->address.host, tmpsv.port); // this peer can be disconnected pretty much immediately since he has no servers left, tell him to fuck off - peer->data = NULL; + sv_clear_peer(peer); enet_peer_disconnect_later(peer, 0); return true; @@ -771,6 +935,7 @@ static bool handle_msg(const enet_uint8 msgid, ENetPeer *peer) { // enet_host_flush(ms_host); // this peer can be disconnected pretty much immediately after receiving the server list, tell him to fuck off + sv_clear_peer(peer); enet_peer_disconnect_later(peer, 0); u_log(LOG_NOTE, "sent server list to %s:%d (ver %s)", u_iptostr(peer->address.host), peer->address.port, clientver[0] ? clientver : ""); @@ -896,8 +1061,8 @@ int main(int argc, char **argv) { atexit(deinit); -#ifdef SIGUSR1 - signal(SIGUSR1, sigusr_handler); +#ifdef ENABLE_PIPE + io_install_pipe(); #endif ENetAddress addr; @@ -913,7 +1078,7 @@ int main(int argc, char **argv) { enet_uint8 msgid = 0; ENetEvent event; while (running) { - while (enet_host_service(ms_host, &event, 500) > 0) { + while (enet_host_service(ms_host, &event, 10) > 0) { const time_t now = time(NULL); const bool filtered = !event.peer || (ms_spam_cap && spam_filter(event.peer, now)); @@ -946,7 +1111,7 @@ int main(int argc, char **argv) { break; case ENET_EVENT_TYPE_DISCONNECT: - event.peer->data = NULL; + // u_log(LOG_NOTE, "%s:%d disconnected", u_iptostr(event.peer->address.host), event.peer->address.port); break; @@ -955,7 +1120,7 @@ int main(int argc, char **argv) { } } else if (event.peer) { // u_log(LOG_WARN, "filtered event %d from %s", event.type, u_iptostr(event.peer->address.host)); - event.peer->data = NULL; + sv_clear_peer(event.peer); enet_peer_reset(event.peer); } @@ -974,5 +1139,10 @@ int main(int argc, char **argv) { sv_remove(servers + i); } } + +#ifdef ENABLE_PIPE + // read commands from pipe + io_read_commands(); +#endif } }