diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0f157f735..515e1ce1d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -152,6 +152,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_hitlag.c k_dialogue.cpp k_tally.cpp + k_bans.cpp music.cpp music_manager.cpp ) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0a2cbf53d..e2198e869 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -64,6 +64,7 @@ #include "k_serverstats.h" #include "k_zvote.h" #include "music.h" +#include "k_bans.h" // cl loading screen #include "v_video.h" @@ -2420,287 +2421,6 @@ static void CL_ConnectToServer(void) } -static void Command_ShowBan(void) //Print out ban list -{ - size_t i; - const char *address, *mask, *reason, *username; - time_t unbanTime = NO_BAN_TIME; - const time_t curTime = time(NULL); - - if (I_GetBanAddress) - CONS_Printf(M_GetText("Ban List:\n")); - else - return; - - for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) - { - unbanTime = NO_BAN_TIME; - if (I_GetUnbanTime) - unbanTime = I_GetUnbanTime(i); - - if (unbanTime != NO_BAN_TIME && curTime >= unbanTime) - continue; - - CONS_Printf("%s: ", sizeu1(i+1)); - - if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL) - CONS_Printf("%s - ", username); - - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - CONS_Printf("%s", address); - else - CONS_Printf("%s/%s", address, mask); - - if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) - CONS_Printf(" - %s", reason); - - if (unbanTime != NO_BAN_TIME) - { - // these are fudged a little to match what a joiner sees - int minutes = ((unbanTime - curTime) + 30) / 60; - int hours = (minutes + 1) / 60; - int days = (hours + 1) / 24; - if (days) - CONS_Printf(" (%d day%s)", days, days > 1 ? "s" : ""); - else if (hours) - CONS_Printf(" (%d hour%s)", hours, hours > 1 ? "s" : ""); - else if (minutes) - CONS_Printf(" (%d minute%s)", minutes, minutes > 1 ? "s" : ""); - else - CONS_Printf(" (<1 minute)"); - } - - CONS_Printf("\n"); - } - - if (i == 0 && !address) - CONS_Printf(M_GetText("(empty)\n")); -} - -static boolean bansLoaded = false; -// If you're a community contributor looking to improve how bans are written, please -// offer your changes back to our Git repository. Kart Krew reserve the right to -// utilise format numbers in use by community builds for different layouts. -#define BANFORMAT 1 - -void D_SaveBan(void) -{ - FILE *f; - size_t i; - const char *address, *mask; - const char *username, *reason; - const time_t curTime = time(NULL); - time_t unbanTime = NO_BAN_TIME; - const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); - - if (bansLoaded != true) - { - // You didn't even get to ATTEMPT to load bans.txt. - // Don't immediately save nothing over it. - return; - } - - f = fopen(path, "w"); - - if (!f) - { - CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n")); - return; - } - - // Add header. - fprintf(f, "BANFORMAT %d\n", BANFORMAT); - - for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) - { - if (I_GetUnbanTime) - { - unbanTime = I_GetUnbanTime(i); - } - else - { - unbanTime = NO_BAN_TIME; - } - - if (unbanTime != NO_BAN_TIME && curTime >= unbanTime) - { - // This one has served their sentence. - // We don't need to save them in the file anymore. - continue; - } - - mask = NULL; - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - fprintf(f, "%s/0", address); - else - fprintf(f, "%s/%s", address, mask); - - // TODO: it'd be nice to convert this to an actual date-time, - // so it'd be easier to edit outside of the game. - fprintf(f, " %ld", (long)unbanTime); - - username = NULL; - if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL) - fprintf(f, " \"%s\"", username); - else - fprintf(f, " \"%s\"", "Direct IP ban"); - - reason = NULL; - if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) - fprintf(f, " \"%s\"\n", reason); - else - fprintf(f, " \"%s\"\n", "No reason given"); - } - - fclose(f); -} - -static void Command_ClearBans(void) -{ - if (!I_ClearBans) - return; - - I_ClearBans(); - D_SaveBan(); -} - -void D_LoadBan(boolean warning) -{ - FILE *f; - size_t i, j; - char *address, *mask; - char *username, *reason; - time_t unbanTime = NO_BAN_TIME; - char buffer[MAX_WADPATH]; - UINT8 banmode = 0; - boolean malformed = false; - - if (!I_ClearBans) - return; - - // We at least attempted loading bans.txt - bansLoaded = true; - - f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); - - if (!f) - { - if (warning) - CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n")); - return; - } - - I_ClearBans(); - - for (i = 0; fgets(buffer, (int)sizeof(buffer), f); i++) - { - address = strtok(buffer, " /\t\r\n"); - mask = strtok(NULL, " \t\r\n"); - - if (i == 0 && !strncmp(address, "BANFORMAT", 9)) - { - if (mask) - { - banmode = atoi(mask); - } - switch (banmode) - { - case BANFORMAT: // currently supported format - //case 0: -- permitted only when BANFORMAT string not present - break; - default: - { - fclose(f); - CONS_Alert(CONS_WARNING, "Could not load unknown ban.txt for ban list (BANFORMAT %s, expected %d)\n", mask, BANFORMAT); - return; - } - } - continue; - } - - if (I_SetBanAddress(address, mask) == false) // invalid IP input? - { - CONS_Alert(CONS_WARNING, "\"%s/%s\" is not a valid IP address, discarding...\n", address, mask); - continue; - } - - // One-way legacy format conversion -- the game will crash otherwise - if (banmode == 0) - { - unbanTime = NO_BAN_TIME; - username = NULL; // not guaranteed to be accurate, but only sane substitute - reason = strtok(NULL, "\r\n"); - if (reason && reason[0] == 'N' && reason[1] == 'A' && reason[2] == '\0') - { - reason = NULL; - } - } - else - { - reason = strtok(NULL, " \"\t\r\n"); - if (reason) - { - unbanTime = atoi(reason); - reason = NULL; - } - else - { - unbanTime = NO_BAN_TIME; - malformed = true; - } - - username = strtok(NULL, "\"\t\r\n"); // go until next " - if (!username) - { - malformed = true; - } - - strtok(NULL, "\"\t\r\n"); // remove first " - reason = strtok(NULL, "\"\r\n"); // go until next " - if (!reason) - { - malformed = true; - } - } - - // Enforce MAX_REASONLENGTH. - if (reason) - { - j = 0; - while (reason[j] != '\0') - { - if ((j++) < MAX_REASONLENGTH) - continue; - reason[j] = '\0'; - break; - } - } - - if (I_SetUnbanTime) - I_SetUnbanTime(unbanTime); - - if (I_SetBanUsername) - I_SetBanUsername(username); - - if (I_SetBanReason) - I_SetBanReason(reason); - } - - if (malformed) - { - CONS_Alert(CONS_WARNING, "One or more lines of ban.txt are malformed. The game can correct for this, but some data may be lost.\n"); - } - - fclose(f); -} - -#undef BANFORMAT - -static void Command_ReloadBan(void) //recheck ban.txt -{ - D_LoadBan(true); -} - static void Command_connect(void) { @@ -3122,71 +2842,6 @@ static void Command_Ban(void) CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); } -static void Command_BanIP(void) -{ - size_t ac = COM_Argc(); - - if (ac < 2) - { - CONS_Printf(M_GetText("banip []: ban an ip address\n")); - return; - } - - if (server) // Only the server can use this, otherwise does nothing. - { - char *addressInput = Z_StrDup(COM_Argv(1)); - - const char *address = NULL; - const char *mask = NULL; - - const char *reason = NULL; - - address = strtok(addressInput, "/"); - mask = strtok(NULL, ""); - - if (ac > 2) - { - reason = COM_Argv(2); - } - - if (I_SetBanAddress && I_SetBanAddress(address, mask)) - { - if (reason) - { - CONS_Printf( - "Banned IP address %s%s for: %s\n", - address, - (mask && (strlen(mask) > 0)) ? va("/%s", mask) : "", - reason - ); - } - else - { - CONS_Printf( - "Banned IP address %s%s\n", - address, - (mask && (strlen(mask) > 0)) ? va("/%s", mask) : "" - ); - } - - if (I_SetUnbanTime) - I_SetUnbanTime(NO_BAN_TIME); - - if (I_SetBanUsername) - I_SetBanUsername(NULL); - - if (I_SetBanReason) - I_SetBanReason(reason); - - D_SaveBan(); - } - else - { - return; - } - } -} - static void Command_Kick(void) { if (COM_Argc() < 2) @@ -3263,6 +2918,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) { READSTRINGN(*p, reason, MAX_REASONLENGTH+1); } + else + { + memset(reason, 0, sizeof(buf)); + } // Is playernum authorized to make this kick? if (playernum != serverplayer && !IsPlayerAdmin(playernum) @@ -3349,28 +3008,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN || banMinutes) { - if (I_Ban && !I_Ban(playernode[(INT32)pnum])) - { - CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); - } - else - { - if (I_SetBanUsername) - I_SetBanUsername(player_names[pnum]); - - if (I_SetBanReason) - I_SetBanReason(reason); - - if (I_SetUnbanTime) - { - if (banMinutes) - I_SetUnbanTime(time(NULL) + (banMinutes * 60)); - else - I_SetUnbanTime(NO_BAN_TIME); - } - - D_SaveBan(); - } + SV_BanPlayer(pnum, banMinutes, reason); } } @@ -3670,10 +3308,9 @@ void D_ClientServerInit(void) COM_AddCommand("getplayernum", Command_GetPlayerNum); COM_AddCommand("kick", Command_Kick); COM_AddCommand("ban", Command_Ban); + COM_AddCommand("listbans", Command_Listbans); + COM_AddCommand("unban", Command_Unban); COM_AddCommand("banip", Command_BanIP); - COM_AddCommand("clearbans", Command_ClearBans); - COM_AddCommand("showbanlist", Command_ShowBan); - COM_AddCommand("reloadbans", Command_ReloadBan); COM_AddCommand("connect", Command_connect); COM_AddCommand("nodes", Command_Nodes); #ifdef HAVE_CURL @@ -3697,7 +3334,6 @@ void D_ClientServerInit(void) CV_RegisterList(cvlist_dumpconsistency); } #endif - D_LoadBan(false); gametic = 0; localgametic = 0; @@ -4460,41 +4096,46 @@ static void HandleConnect(SINT8 node) if (playernode[i] != UINT8_MAX) // We use this to count players because it is affected by SV_AddWaitingPlayers when more than one client joins on the same tic, unlike playeringame and D_NumPlayers. UINT8_MAX denotes no node for that player connectedplayers++; - if (bannednode && bannednode[node].banid != SIZE_MAX) + banrecord_t *ban = SV_GetBanByAddress(node); + if (ban == NULL) { - const char *reason = NULL; - - // Get the reason... - if (!I_GetBanReason || (reason = I_GetBanReason(bannednode[node].banid)) == NULL) - reason = "No reason given"; - - if (bannednode[node].timeleft != NO_BAN_TIME) + for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++) { - // these are fudged a little to allow it to sink in for impatient rejoiners - int minutes = (bannednode[node].timeleft + 30) / 60; + if (ban == NULL) + ban = SV_GetBanByKey(lastReceivedKey[node][i]); + } + } + + if (ban != NULL && node != 0) + { + UINT32 timeremaining = 0; + if (ban->expires > time(NULL)) + { + timeremaining = ban->expires - time(NULL); + int minutes = (timeremaining + 30) / 60; int hours = (minutes + 1) / 60; int days = (hours + 1) / 24; if (days) { - SV_SendRefuse(node, va("K|%s\n(Time remaining: %d day%s)", reason, days, days > 1 ? "s" : "")); + SV_SendRefuse(node, va("K|%s\n(Time remaining: %d day%s)", ban->reason, days, days > 1 ? "s" : "")); } else if (hours) { - SV_SendRefuse(node, va("K|%s\n(Time remaining: %d hour%s)", reason, hours, hours > 1 ? "s" : "")); + SV_SendRefuse(node, va("K|%s\n(Time remaining: %d hour%s)", ban->reason, hours, hours > 1 ? "s" : "")); } else if (minutes) { - SV_SendRefuse(node, va("K|%s\n(Time remaining: %d minute%s)", reason, minutes, minutes > 1 ? "s" : "")); + SV_SendRefuse(node, va("K|%s\n(Time remaining: %d minute%s)", ban->reason, minutes, minutes > 1 ? "s" : "")); } else { - SV_SendRefuse(node, va("K|%s\n(Time remaining: <1 minute)", reason)); + SV_SendRefuse(node, va("K|%s\n(Time remaining: <1 minute)", ban->reason)); } } else { - SV_SendRefuse(node, va("B|%s", reason)); + SV_SendRefuse(node, va("B|%s", ban->reason)); } } else if (netbuffer->u.clientcfg._255 != 255 || diff --git a/src/d_main.cpp b/src/d_main.cpp index 2343a837c..8b258c7bc 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -87,6 +87,7 @@ #include "k_serverstats.h" #include "music.h" #include "k_dialogue.h" +#include "k_bans.h" #ifdef HWRENDER #include "hardware/hw_main.h" // 3D View Rendering @@ -1609,6 +1610,7 @@ void D_SRB2Main(void) PR_LoadProfiles(); // load control profiles SV_LoadStats(); + SV_LoadBans(); #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen @@ -1924,6 +1926,7 @@ void D_SRB2Main(void) } SV_SaveStats(); + SV_SaveBans(); if (autostart || netgame) { diff --git a/src/d_net.c b/src/d_net.c index 50f9d2d2a..ef48a55ab 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -81,19 +81,8 @@ SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL; void (*I_NetRequestHolePunch)(INT32 node) = NULL; void (*I_NetRegisterHolePunch)(void) = NULL; boolean (*I_NetOpenSocket)(void) = NULL; -boolean (*I_Ban) (INT32 node) = NULL; -void (*I_ClearBans)(void) = NULL; const char *(*I_GetNodeAddress) (INT32 node) = NULL; UINT32 (*I_GetNodeAddressInt) (INT32 node) = NULL; -const char *(*I_GetBanAddress) (size_t ban) = NULL; -const char *(*I_GetBanMask) (size_t ban) = NULL; -const char *(*I_GetBanUsername) (size_t ban) = NULL; -const char *(*I_GetBanReason) (size_t ban) = NULL; -time_t (*I_GetUnbanTime) (size_t ban) = NULL; -boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL; -boolean (*I_SetBanUsername) (const char *username) = NULL; -boolean (*I_SetBanReason) (const char *reason) = NULL; -boolean (*I_SetUnbanTime) (time_t timestamp) = NULL; boolean (*I_IsExternalAddress) (const void *p) = NULL; bannednode_t *bannednode = NULL; diff --git a/src/d_net.h b/src/d_net.h index 5cf4b9f77..90283ce6c 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -58,8 +58,6 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength); boolean HGetPacket(void); void D_SetDoomcom(void); -void D_SaveBan(void); -void D_LoadBan(boolean warning); boolean D_CheckNetGame(void); void D_CloseConnection(void); void Net_UnAcknowledgePacket(INT32 node); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index acb00e589..b51f1408a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -69,6 +69,7 @@ #include "k_bot.h" #include "k_powerup.h" #include "k_roulette.h" +#include "k_bans.h" #ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES #include "m_avrecorder.h" diff --git a/src/i_net.h b/src/i_net.h index 5755f6169..b8792bbdf 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -163,19 +163,8 @@ extern void (*I_NetRequestHolePunch)(INT32 node); extern void (*I_NetRegisterHolePunch)(void); -extern boolean (*I_Ban) (INT32 node); -extern void (*I_ClearBans)(void); extern const char *(*I_GetNodeAddress) (INT32 node); extern UINT32 (*I_GetNodeAddressInt) (INT32 node); -extern const char *(*I_GetBanAddress) (size_t ban); -extern const char *(*I_GetBanMask) (size_t ban); -extern const char *(*I_GetBanUsername) (size_t ban); -extern const char *(*I_GetBanReason) (size_t ban); -extern time_t (*I_GetUnbanTime) (size_t ban); -extern boolean (*I_SetBanAddress) (const char *address,const char *mask); -extern boolean (*I_SetBanUsername) (const char *username); -extern boolean (*I_SetBanReason) (const char *reason); -extern boolean (*I_SetUnbanTime) (time_t timestamp); extern boolean (*I_IsExternalAddress) (const void *p); struct bannednode_t diff --git a/src/i_tcp.c b/src/i_tcp.c index 18e79fbd6..bf01cea81 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -12,120 +12,12 @@ /// This is not really OS-dependent because all OSes have the same socket API. /// Just use ifdef for OS-dependent parts. -#include -#include -#include -#ifdef __GNUC__ -#include -#endif - -#ifndef NO_IPV6 - #define HAVE_IPV6 -#endif - -#ifdef _WIN32 - #define USE_WINSOCK - #if defined (_WIN64) || defined (HAVE_IPV6) - #define USE_WINSOCK2 - #else //_WIN64/HAVE_IPV6 - #define USE_WINSOCK1 - #endif -#endif //WIN32 OS - -#ifdef USE_WINSOCK2 - #include -#endif - -#include "doomdef.h" - -#ifdef USE_WINSOCK1 - #include -#else - #ifndef USE_WINSOCK - #include - #ifdef __APPLE_CC__ - #ifndef _BSD_SOCKLEN_T_ - #define _BSD_SOCKLEN_T_ - #endif //_BSD_SOCKLEN_T_ - #endif //__APPLE_CC__ - #include - #include - #include - #include - #endif //normal BSD API - - #include - #include - - #if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) - #include - #endif // UNIXCOMMON -#endif - -#ifdef USE_WINSOCK - // some undefined under win32 - #undef errno - //#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right? - #define errno h_errno // some very strange things happen when not using h_error?!? - #ifdef EWOULDBLOCK - #undef EWOULDBLOCK - #endif - #define EWOULDBLOCK WSAEWOULDBLOCK - #ifdef EMSGSIZE - #undef EMSGSIZE - #endif - #define EMSGSIZE WSAEMSGSIZE - #ifdef ECONNREFUSED - #undef ECONNREFUSED - #endif - #define ECONNREFUSED WSAECONNREFUSED - #ifdef ETIMEDOUT - #undef ETIMEDOUT - #endif - #define ETIMEDOUT WSAETIMEDOUT - #ifndef IOC_VENDOR - #define IOC_VENDOR 0x18000000 - #endif - #ifndef _WSAIOW - #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) - #endif - #ifndef SIO_UDP_CONNRESET - #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) - #endif - #ifndef AI_ADDRCONFIG - #define AI_ADDRCONFIG 0x00000400 - #endif - #ifndef STATUS_INVALID_PARAMETER - #define STATUS_INVALID_PARAMETER 0xC000000D - #endif -#endif // USE_WINSOCK - -typedef union -{ - struct sockaddr any; - struct sockaddr_in ip4; -#ifdef HAVE_IPV6 - struct sockaddr_in6 ip6; -#endif -} mysockaddr_t; - -#ifdef HAVE_MINIUPNPC - #ifdef STATIC_MINIUPNPC - #define STATICLIB - #endif - #include "miniupnpc/miniwget.h" - #include "miniupnpc/miniupnpc.h" - #include "miniupnpc/upnpcommands.h" - #undef STATICLIB - static UINT8 UPNP_support = TRUE; -#endif // HAVE_MINIUPNC - +#include "i_tcp_detail.h" #include "i_system.h" #include "i_time.h" #include "i_net.h" #include "d_net.h" #include "d_netfil.h" -#include "i_tcp.h" #include "m_argv.h" #include "stun.h" #include "z_zone.h" @@ -175,16 +67,12 @@ static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; static size_t mysocketses = 0; static int myfamily[MAXNETNODES+1] = {0}; static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET}; -static mysockaddr_t clientaddress[MAXNETNODES+1]; +mysockaddr_t clientaddress[MAXNETNODES+1]; static mysockaddr_t broadcastaddress[MAXNETNODES+1]; static size_t broadcastaddresses = 0; static boolean nodeconnected[MAXNETNODES+1]; -static banned_t *banned; static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11); -static size_t numbans = 0; -static size_t banned_size = 0; - static bannednode_t SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? static boolean init_tcp_driver = false; @@ -341,7 +229,14 @@ static inline void I_UPnP_rem(const char *port, const char * servicetype) } #endif -static const char *SOCK_AddrToStr(mysockaddr_t *sk) +// I spent 3 hours trying to work out why I couldn't sensibly access this from anywhere, +// even when extern'd. I am not the person who should be doing this. -Tyron, 2023-10-08 +mysockaddr_t SOCK_DirectNodeToAddr(UINT8 node) +{ + return clientaddress[node]; +} + +const char *SOCK_AddrToStr(mysockaddr_t *sk) { static char s[64]; // 255.255.255.255:65535 or IPv6:65535 #ifdef HAVE_NTOP @@ -401,45 +296,7 @@ static UINT32 SOCK_GetNodeAddressInt(INT32 node) return 0; } -static const char *SOCK_GetBanAddress(size_t ban) -{ - if (ban >= numbans) - return NULL; - return SOCK_AddrToStr(&banned[ban].address); -} - -static const char *SOCK_GetBanMask(size_t ban) -{ - static char s[16]; //255.255.255.255 netmask? no, just CDIR for only - if (ban >= numbans) - return NULL; - if (sprintf(s,"%d",banned[ban].mask) > 0) - return s; - return NULL; -} - -static const char *SOCK_GetBanUsername(size_t ban) -{ - if (ban >= numbans) - return NULL; - return banned[ban].username; -} - -static const char *SOCK_GetBanReason(size_t ban) -{ - if (ban >= numbans) - return NULL; - return banned[ban].reason; -} - -static time_t SOCK_GetUnbanTime(size_t ban) -{ - if (ban >= numbans) - return NO_BAN_TIME; - return banned[ban].timestamp; -} - -static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) +boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) { UINT32 bitmask = INADDR_NONE; @@ -565,7 +422,7 @@ static boolean hole_punch(ssize_t c) // Returns true if a packet was received from a new node, false in all other cases static boolean SOCK_Get(void) { - size_t i, n; + size_t n; int j; ssize_t c; mysockaddr_t fromaddress; @@ -607,8 +464,6 @@ static boolean SOCK_Get(void) j = getfreenode(); if (j > 0) { - const time_t curTime = time(NULL); - M_Memcpy(&clientaddress[j], &fromaddress, fromlen); nodesocket[j] = mysockets[n]; DEBFILE(va("New node detected: node:%d address:%s\n", j, @@ -616,42 +471,6 @@ static boolean SOCK_Get(void) doomcom->remotenode = (INT16)j; // good packet from a game player doomcom->datalength = (INT16)c; - // check if it's a banned dude so we can send a refusal later - for (i = 0; i < numbans; i++) - { - if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask)) - { - if (banned[i].timestamp != NO_BAN_TIME) - { - if (curTime >= banned[i].timestamp) - { - SOCK_bannednode[j].timeleft = NO_BAN_TIME; - SOCK_bannednode[j].banid = SIZE_MAX; - DEBFILE("This dude was banned, but enough time has passed\n"); - break; - } - - SOCK_bannednode[j].timeleft = banned[i].timestamp - curTime; - SOCK_bannednode[j].banid = i; - DEBFILE("This dude has been temporarily banned\n"); - break; - } - else - { - SOCK_bannednode[j].timeleft = NO_BAN_TIME; - SOCK_bannednode[j].banid = i; - DEBFILE("This dude has been banned\n"); - break; - } - } - } - - if (i == numbans) - { - SOCK_bannednode[j].timeleft = NO_BAN_TIME; - SOCK_bannednode[j].banid = SIZE_MAX; - } - return true; } else @@ -1357,171 +1176,6 @@ static boolean SOCK_OpenSocket(void) return UDP_Socket(); } -static void AddBannedIndex(void) -{ - if (numbans >= banned_size) - { - if (banned_size == 0) - { - banned_size = 8; - } - else - { - banned_size *= 2; - } - - banned = Z_ReallocAlign( - (void*) banned, - sizeof(banned_t) * banned_size, - PU_STATIC, - NULL, - sizeof(banned_t) * 8 - ); - } - - numbans++; -} - -static boolean SOCK_Ban(INT32 node) -{ - INT32 ban; - - if (node > MAXNETNODES) - return false; - - ban = numbans; - AddBannedIndex(); - - M_Memcpy(&banned[ban].address, &clientaddress[node], sizeof (mysockaddr_t)); - - if (banned[ban].address.any.sa_family == AF_INET) - { - banned[ban].address.ip4.sin_port = 0; - banned[ban].mask = 32; - } -#ifdef HAVE_IPV6 - else if (banned[ban].address.any.sa_family == AF_INET6) - { - banned[ban].address.ip6.sin6_port = 0; - banned[ban].mask = 128; - } -#endif - - return true; -} - -static boolean SOCK_SetBanUsername(const char *username) -{ - if (username == NULL || strlen(username) == 0) - { - username = "Direct IP ban"; - } - - if (banned[numbans - 1].username) - { - Z_Free(banned[numbans - 1].username); - banned[numbans - 1].username = NULL; - } - - banned[numbans - 1].username = Z_StrDup(username); - return true; -} - -static boolean SOCK_SetBanReason(const char *reason) -{ - if (reason == NULL || strlen(reason) == 0) - { - reason = "No reason given"; - } - - if (banned[numbans - 1].reason) - { - Z_Free(banned[numbans - 1].reason); - banned[numbans - 1].reason = NULL; - } - - banned[numbans - 1].reason = Z_StrDup(reason); - return true; -} - -static boolean SOCK_SetUnbanTime(time_t timestamp) -{ - banned[numbans - 1].timestamp = timestamp; - return true; -} - -static boolean SOCK_SetBanAddress(const char *address, const char *mask) -{ - struct my_addrinfo *ai, *runp, hints; - int gaie; - - if (!address) - return false; - - memset(&hints, 0x00, sizeof(hints)); - hints.ai_flags = 0; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - - gaie = I_getaddrinfo(address, "0", &hints, &ai); - if (gaie != 0) - return false; - - runp = ai; - - while (runp != NULL) - { - INT32 ban; - UINT8 numericalmask; - - ban = numbans; - AddBannedIndex(); - - memcpy(&banned[ban].address, runp->ai_addr, runp->ai_addrlen); - -#ifdef HAVE_IPV6 - if (runp->ai_family == AF_INET6) - banned[ban].mask = 128; - else -#endif - banned[ban].mask = 32; - - if (mask) - { - numericalmask = (UINT8)atoi(mask); - } - else - { - numericalmask = 0; - } - - if (numericalmask > 0 && numericalmask < banned[ban].mask) - { - banned[ban].mask = numericalmask; - } - - // Set defaults, in case anything funny happens. - SOCK_SetBanUsername(NULL); - SOCK_SetBanReason(NULL); - SOCK_SetUnbanTime(NO_BAN_TIME); - - runp = runp->ai_next; - } - - I_freeaddrinfo(ai); - - return true; -} - -static void SOCK_ClearBans(void) -{ - numbans = 0; - banned_size = 0; - Z_Free(banned); - banned = NULL; -} - // https://github.com/jameds/holepunch/blob/master/holepunch.c#L75 static int SOCK_IsExternalAddress (const void *p) { @@ -1634,19 +1288,8 @@ boolean I_InitTcpNetwork(void) } I_NetOpenSocket = SOCK_OpenSocket; - I_Ban = SOCK_Ban; - I_ClearBans = SOCK_ClearBans; I_GetNodeAddress = SOCK_GetNodeAddress; I_GetNodeAddressInt = SOCK_GetNodeAddressInt; - I_GetBanAddress = SOCK_GetBanAddress; - I_GetBanMask = SOCK_GetBanMask; - I_GetBanUsername = SOCK_GetBanUsername; - I_GetBanReason = SOCK_GetBanReason; - I_GetUnbanTime = SOCK_GetUnbanTime; - I_SetBanAddress = SOCK_SetBanAddress; - I_SetBanUsername = SOCK_SetBanUsername; - I_SetBanReason = SOCK_SetBanReason; - I_SetUnbanTime = SOCK_SetUnbanTime; I_IsExternalAddress = SOCK_IsExternalAddress; bannednode = SOCK_bannednode; diff --git a/src/i_tcp.h b/src/i_tcp.h index 2bbe96fbd..b5bdc8a4d 100644 --- a/src/i_tcp.h +++ b/src/i_tcp.h @@ -17,6 +17,8 @@ extern "C" { #endif +#include "d_net.h" + extern UINT16 current_port; /** \brief The I_InitTcpNetwork function diff --git a/src/i_tcp_detail.h b/src/i_tcp_detail.h new file mode 100644 index 000000000..a0ec63a9d --- /dev/null +++ b/src/i_tcp_detail.h @@ -0,0 +1,140 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef i_tcp_detail_h +#define i_tcp_detail_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#ifdef __GNUC__ +#include +#endif + +#ifndef NO_IPV6 + #define HAVE_IPV6 +#endif + +#ifdef _WIN32 + #define USE_WINSOCK + #if defined (_WIN64) || defined (HAVE_IPV6) + #define USE_WINSOCK2 + #else //_WIN64/HAVE_IPV6 + #define USE_WINSOCK1 + #endif +#endif //WIN32 OS + +#ifdef USE_WINSOCK2 + #include +#endif + +#include "doomdef.h" + +#ifdef USE_WINSOCK1 + #include +#else + #ifndef USE_WINSOCK + #include + #ifdef __APPLE_CC__ + #ifndef _BSD_SOCKLEN_T_ + #define _BSD_SOCKLEN_T_ + #endif //_BSD_SOCKLEN_T_ + #endif //__APPLE_CC__ + #include + #include + #include + #include + #endif //normal BSD API + + #include + #include + + #if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) + #include + #endif // UNIXCOMMON +#endif + +#ifdef USE_WINSOCK + // some undefined under win32 + #undef errno + //#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right? + #define errno h_errno // some very strange things happen when not using h_error?!? + #ifdef EWOULDBLOCK + #undef EWOULDBLOCK + #endif + #define EWOULDBLOCK WSAEWOULDBLOCK + #ifdef EMSGSIZE + #undef EMSGSIZE + #endif + #define EMSGSIZE WSAEMSGSIZE + #ifdef ECONNREFUSED + #undef ECONNREFUSED + #endif + #define ECONNREFUSED WSAECONNREFUSED + #ifdef ETIMEDOUT + #undef ETIMEDOUT + #endif + #define ETIMEDOUT WSAETIMEDOUT + #ifndef IOC_VENDOR + #define IOC_VENDOR 0x18000000 + #endif + #ifndef _WSAIOW + #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) + #endif + #ifndef SIO_UDP_CONNRESET + #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) + #endif + #ifndef AI_ADDRCONFIG + #define AI_ADDRCONFIG 0x00000400 + #endif + #ifndef STATUS_INVALID_PARAMETER + #define STATUS_INVALID_PARAMETER 0xC000000D + #endif +#endif // USE_WINSOCK + +#ifdef HAVE_MINIUPNPC + #ifdef STATIC_MINIUPNPC + #define STATICLIB + #endif + #include "miniupnpc/miniwget.h" + #include "miniupnpc/miniupnpc.h" + #include "miniupnpc/upnpcommands.h" + #undef STATICLIB + static UINT8 UPNP_support = TRUE; +#endif // HAVE_MINIUPNC + +#include "d_net.h" +#include "doomtype.h" +#include "i_tcp.h" + +union mysockaddr_t +{ + struct sockaddr any; + struct sockaddr_in ip4; +#ifdef HAVE_IPV6 + struct sockaddr_in6 ip6; +#endif +}; + +extern mysockaddr_t clientaddress[MAXNETNODES+1]; + +const char *SOCK_AddrToStr(mysockaddr_t *sk); +mysockaddr_t SOCK_DirectNodeToAddr(UINT8 node); +boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif/*i_tcp_detail_h*/ diff --git a/src/k_bans.cpp b/src/k_bans.cpp new file mode 100644 index 000000000..1f8d17511 --- /dev/null +++ b/src/k_bans.cpp @@ -0,0 +1,439 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 2023 by AJ "Tyron" Martinez +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_bans.c +/// \brief replacement for DooM Legacy ban system + +#include +#include +#include + +#include +#include + +#include "i_tcp_detail.h" // clientaddress +#include "k_bans.h" +#include "byteptr.h" // READ/WRITE macros +#include "command.h" +#include "d_clisrv.h" // playernode +#include "d_main.h" // pandf +#include "d_net.h" // MAXNETNODES +#include "doomtype.h" +#include "k_profiles.h" +#include "m_misc.h" //FIL_WriteFile() +#include "p_saveg.h" // savebuffer_t +#include "time.h" +#include "z_zone.h" +#include "i_addrinfo.h" // I_getaddrinfo + +using nlohmann::json; + +static mysockaddr_t *DuplicateSockAddr(const mysockaddr_t *source) +{ + auto dup = static_cast(Z_Malloc(sizeof *source, PU_STATIC, NULL)); + memcpy(dup, source, sizeof *source); + return dup; +} + +static std::vector bans; + +static uint8_t allZero[PUBKEYLENGTH]; + +static void load_bans_array_v1(json& array) +{ + for (json& object : array) + { + uint8_t public_key_bin[PUBKEYLENGTH]; + + std::string public_key = object.at("public_key"); + std::string ip_address = object.at("ip_address"); + time_t expires = object.at("expires"); + UINT8 subnet_mask = object.at("subnet_mask"); + std::string username = object.at("username"); + std::string reason = object.at("reason"); + + if (!FromPrettyRRID(public_key_bin, public_key.c_str())) + { + throw std::runtime_error("public_key has invalid format"); + } + + bool banned = SV_BanIP( + ip_address.c_str(), + subnet_mask, + public_key_bin, + expires, + username.c_str(), + reason.c_str() + ); + + if (!banned) + { + throw std::runtime_error(fmt::format("{}: IP address is invalid", ip_address)); + } + } +} + +void SV_LoadBans(void) +{ + if (!server) + return; + + json object; + + try + { + std::ifstream f(va(pandf, srb2home, BANFILE)); + + if (f.is_open()) + { + f >> object; + } + } + catch (const std::exception& ex) + { + const char* gdfolder = "the Ring Racers folder"; + if (strcmp(srb2home, ".")) + gdfolder = srb2home; + + I_Error("%s\nNot a valid ban file.\nDelete %s (maybe in %s) and try again.", ex.what(), BANFILE, gdfolder); + } + + if (!object.is_object()) + { + return; + } + + try + { + if (object.value("version", 1) == 1) + { + json& array = object.at("bans"); + + if (array.is_array()) + { + load_bans_array_v1(array); + } + } + } + catch (const std::exception& ex) + { + I_Error("%s: %s", BANFILE, ex.what()); + } +} + +void SV_SaveBans(void) +{ + json object = json::object(); + + object["version"] = 1; + json& array = object["bans"]; + + array = json::array(); + + for (banrecord_t& ban : bans) + { + if (ban.deleted) + continue; + + array.push_back({ + {"public_key", GetPrettyRRID(ban.public_key, false)}, + {"ip_address", SOCK_AddrToStr(ban.address)}, + {"subnet_mask", ban.mask}, + {"expires", ban.expires}, + {"username", ban.username}, + {"reason", ban.reason}, + }); + } + + try + { + std::ofstream(va(pandf, srb2home, BANFILE)) << object; + } + catch (const std::exception& ex) + { + I_Error("%s\nCouldn't save bans. Are you out of disk space / playing in a protected folder?", ex.what()); + } +} + +mysockaddr_t convertedaddress; +static mysockaddr_t* SV_NodeToBanAddress(UINT8 node) +{ + convertedaddress = SOCK_DirectNodeToAddr(node); + + if (convertedaddress.any.sa_family == AF_INET) + { + convertedaddress.ip4.sin_port = 0; + // mask was 32? + } +#ifdef HAVE_IPV6 + else if (convertedaddress.any.sa_family == AF_INET6) + { + convertedaddress.ip6.sin6_port = 0; + // mask was 128? + } +#endif + + return &convertedaddress; +} + +static boolean SV_IsBanEnforced(banrecord_t *ban) +{ + if (ban->deleted) + return false; + if ((ban->expires != 0) && (time(NULL) > ban->expires)) + return false; + return true; +} + +banrecord_t* SV_GetBanByAddress(UINT8 node) +{ + mysockaddr_t* address = SV_NodeToBanAddress(node); + + for (banrecord_t& ban : bans) + { + if (!SV_IsBanEnforced(&ban)) + continue; + if (SOCK_cmpaddr(ban.address, address, ban.mask)) + return &ban; + } + + return NULL; +} + +banrecord_t* SV_GetBanByKey(uint8_t* key) +{ + UINT32 hash; + + hash = quickncasehash((char*) key, PUBKEYLENGTH); + + for (banrecord_t& ban : bans) + { + if (!SV_IsBanEnforced(&ban)) + continue; + if (hash != ban.hash) // Not crypto magic, just an early out with a faster comparison + continue; + if (memcmp(&ban.public_key, allZero, PUBKEYLENGTH) == 0) + continue; // Don't ban GUESTs on accident, we have a cvar for this. + if (memcmp(&ban.public_key, key, PUBKEYLENGTH) == 0) + return &ban; + } + + return NULL; +} + +void SV_BanPlayer(INT32 pnum, time_t minutes, char* reason) +{ + mysockaddr_t targetaddress; + UINT8 node = playernode[pnum]; + time_t expires = 0; + + memcpy(&targetaddress, SV_NodeToBanAddress(node), sizeof(mysockaddr_t)); + + if (minutes != 0) + expires = time(NULL) + minutes * 60; + + SV_Ban(targetaddress, 0, players[pnum].public_key, expires, player_names[pnum], reason); +} + +boolean SV_BanIP(const char *address, UINT8 mask, uint8_t* public_key, time_t expires, const char* username, const char* reason) +{ + struct my_addrinfo *ai, *runp, hints; + + memset(&hints, 0x00, sizeof(hints)); + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + int gaie; + gaie = I_getaddrinfo(address, "0", &hints, &ai); + + if (gaie != 0) + return false; + + runp = ai; + + mysockaddr_t newaddr; + memcpy(&newaddr, runp->ai_addr, runp->ai_addrlen); + + SV_Ban(newaddr, mask, public_key, expires, username, reason); + + return true; +} + +static char noreason[] = "N/A"; +static char nouser[] = "[Direct IP ban]"; +void SV_Ban(mysockaddr_t address, UINT8 mask, uint8_t* public_key, time_t expires, const char* username, const char* reason) +{ + if (reason == NULL || strlen(reason) == 0) + reason = noreason; + if (!public_key) + public_key = allZero; + if (!expires) + expires = 0; + if (!username) + username = nouser; + + banrecord_t ban{}; + + ban.address = DuplicateSockAddr(&address); + memcpy(ban.public_key, public_key, PUBKEYLENGTH); + memcpy(&ban.expires, &expires, sizeof(time_t)); + ban.mask = mask; + strlcpy(ban.username, username, MAXBANUSERNAME); + strlcpy(ban.reason, reason, MAXBANREASON); + + ban.hash = quickncasehash((char*) ban.public_key, PUBKEYLENGTH); + + bans.push_back(ban); + + SV_SaveBans(); +} + +static void SV_BanSearch(boolean remove) +{ + const char* filtertarget = (COM_Argc() > 1) ? COM_Argv(1) : NULL; + + if (remove && !filtertarget) + { + CONS_Printf("unban : removes all bans matching target username, key, or address\n"); + return; + } + + time_t now = time(NULL); + UINT32 records = 0; + UINT32 matchedrecords = 0; + boolean force = COM_CheckPartialParm("-f"); + + // First pass: What records are we even looking for? + for (banrecord_t& ban : bans) + { + ban.matchesquery = false; + + if (ban.deleted) + continue; + + records++; + + const char* stringaddress = SOCK_AddrToStr(ban.address); + const char* stringkey = GetPrettyRRID(ban.public_key, true); + + if (filtertarget != NULL && !(strcasestr(filtertarget, ban.username) || strcasestr(filtertarget, stringkey) || + strcasestr(filtertarget, stringaddress))) + continue; + + ban.matchesquery = true; + matchedrecords++; + } + + boolean saferemove = remove && (matchedrecords <= 1 || force); + + // Second pass: Report and/or act on records. + for (banrecord_t& ban : bans) + { + if (!ban.matchesquery) + continue; + + const char* stringaddress = SOCK_AddrToStr(ban.address); + const char* stringkey = GetPrettyRRID(ban.public_key, true); + + std::string recordprint = fmt::format( + "{}{} - {} [{}] - {}", + stringaddress, + ban.mask && ban.mask != 32 ? fmt::format("/{}", ban.mask) : "", + ban.username, + stringkey, + ban.reason + ); + + if (ban.expires) + { + if (ban.expires < now) + recordprint += " - EXPIRED"; + else + recordprint += fmt::format(" - expires {}m", (ban.expires - now)/60); + } + + CONS_Printf("%s\n", recordprint.c_str()); + + if (saferemove) + ban.deleted = true; + } + + if (records == 0) + CONS_Printf("You haven't banned anyone yet.\n"); + else if (matchedrecords == 0) + CONS_Printf("\x82""No matches in %d bans.\n", records); + else if (remove && !saferemove) + CONS_Printf("\x82""Didn't unban, would remove %d bans.\x80 Refine search or use 'unban -f'.\n", matchedrecords); + else if (saferemove) + CONS_Printf("\x83""Removed %d/%d bans.\n", matchedrecords, records); + else if (filtertarget) + CONS_Printf("Matched %d/%d bans.\n", matchedrecords, records); + else + CONS_Printf("Showing %d bans. Try 'listbans [search]' to refine results.\n", matchedrecords); + + if (saferemove) + SV_SaveBans(); +} + +void Command_Listbans(void) +{ + if (netgame && consoleplayer != serverplayer) + { + CONS_Printf("You must be the netgame host to do this.\n"); + return; + } + SV_BanSearch(false); +} + +void Command_Unban(void) +{ + if (netgame && consoleplayer != serverplayer) + { + CONS_Printf("You must be the netgame host to do this.\n"); + return; + } + SV_BanSearch(true); +} + +void Command_BanIP(void) +{ + if (COM_Argc() <= 1) + { + CONS_Printf("banip [reason]: bans the specified IP from netgames you host\n"); + return; + } + + char* input = Z_StrDup(COM_Argv(1)); + + const char *address = strtok(input, "/"); + const char *mask = strtok(NULL, ""); + + if (!mask) + mask = ""; + + char reason[MAXBANREASON+1] = ""; + if (COM_Argc() > 2) + strncpy(reason, COM_Argv(2), MAXBANREASON); + + UINT8 nummask = atoi(mask); + + if (SV_BanIP(address, nummask, NULL, 0, NULL, reason)) + { + CONS_Printf("Banned address %s.\n", address); + } + else + { + CONS_Printf("Not a valid IP address.\n"); + } + + Z_Free(input); +} diff --git a/src/k_bans.h b/src/k_bans.h new file mode 100644 index 000000000..d97c2fbf5 --- /dev/null +++ b/src/k_bans.h @@ -0,0 +1,62 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 2023 by AJ "Tyron" Martinez +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_bans.h +/// \brief ban system definitions + +#ifndef __BANS_H__ +#define __BANS_H__ + +#include "doomdef.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define BANFILE "srvbans.json" + +#define MAXBANUSERNAME 64 +#define MAXBANREASON 256 + +struct banrecord_t +{ + uint8_t public_key[PUBKEYLENGTH]; + mysockaddr_t *address; + UINT8 mask; + + time_t expires; + + char username[MAXBANUSERNAME+1]; + char reason[MAXBANREASON+1]; + + UINT32 hash; // Not persisted! Used for early outs during key comparisons + boolean deleted; // Not persisted! Deleted records are ignored and not written back to file. + boolean matchesquery; // Not persisted! Used when filtering listbans/unban searches. +}; + +banrecord_t *SV_GetBanByKey(uint8_t *key); +banrecord_t *SV_GetBanByAddress(UINT8 node); + +void SV_LoadBans(void); +void SV_SaveBans(void); +void SV_BanPlayer(int pnum, time_t duration, char *reason); +boolean SV_BanIP(const char *address, UINT8 mask, uint8_t *public_key, time_t expires, const char *username, const char *reason); +void SV_Ban(mysockaddr_t address, UINT8 mask, uint8_t *public_key, time_t expires, const char *username, const char *reason); + +void Command_Listbans(void); +void Command_Unban(void); +void Command_BanIP(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/src/k_profiles.c b/src/k_profiles.c index 5cd98cf7f..f1ff15210 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -627,6 +627,28 @@ char *GetPrettyRRID(const unsigned char *bin, boolean brief) return rrid_buf; } +unsigned char *FromPrettyRRID(unsigned char *bin, const char *text) +{ + size_t i; + size_t len = PUBKEYLENGTH * 2; + + if (strlen(text) != len) + return NULL; + + for (i = 0; i < len; i += 2) + { + char byt[3] = { text[i], text[i+1], '\0' }; + char *p; + + bin[i/2] = strtol(byt, &p, 16); + + if (*p) // input is not hexadecimal + return NULL; + } + + return bin; +} + static char allZero[PUBKEYLENGTH]; diff --git a/src/k_profiles.h b/src/k_profiles.h index 8fc72212f..3b941500f 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -162,6 +162,7 @@ profile_t *PR_GetLocalPlayerProfile(INT32 player); boolean PR_IsLocalPlayerGuest(INT32 player); char *GetPrettyRRID(const unsigned char *bin, boolean brief); +unsigned char *FromPrettyRRID(unsigned char *bin, const char *text); boolean PR_IsKeyGuest(uint8_t *key); diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 3bdd1d7d6..7ac610b0d 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -105,7 +105,7 @@ void SV_LoadStats(void) } else if (version < SERVERSTATSVER) { - // We're converting - let'd create a backup. + // We're converting - let's create a backup. FIL_WriteFile(va("%s" PATHSEP "%s.bak", srb2home, SERVERSTATSFILE), save.buffer, save.size); } @@ -173,7 +173,7 @@ void SV_SaveStats(void) if (!FIL_WriteFile(va(pandf, srb2home, SERVERSTATSFILE), save.buffer, length)) { P_SaveBufferFree(&save); - I_Error("Couldn't save server stats. Are you out of Disk space / playing in a protected folder?"); + I_Error("Couldn't save server stats. Are you out of disk space / playing in a protected folder?"); } P_SaveBufferFree(&save); } diff --git a/src/sdl/i_net.c b/src/sdl/i_net.c index ee4a34c13..b85c052ef 100644 --- a/src/sdl/i_net.c +++ b/src/sdl/i_net.c @@ -422,8 +422,6 @@ boolean I_InitNetwork(void) mypacket.maxlen = hardware_MAXPACKETLENGTH; I_NetOpenSocket = NET_OpenSocket; - I_Ban = NET_Ban; - I_ClearBans = NET_ClearBans; I_GetNodeAddress = NET_GetNodeAddress; I_GetBenAddress = NET_GetBenAddress; I_SetBanAddress = NET_SetBanAddress; diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 5bd806cae..733bd0b61 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1620,7 +1620,6 @@ void I_Quit(void) SDLforceUngrabMouse(); quiting = SDL_FALSE; M_SaveConfig(NULL); //save game config, cvars.. - D_SaveBan(); // save the ban list M_SaveJoinedIPs(); // Make sure you lose points for ALT-F4 @@ -1752,7 +1751,6 @@ void I_Error(const char *error, ...) // --- M_SaveConfig(NULL); // save game config, cvars.. - D_SaveBan(); // save the ban list G_DirtyGameData(); // done first in case an error is in G_SaveGameData G_SaveGameData(); // Tails 12-08-2002 diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c index 0f633f290..cdcbdd1d7 100644 --- a/src/sdl12/i_system.c +++ b/src/sdl12/i_system.c @@ -2981,9 +2981,6 @@ void I_Quit(void) quiting = SDL_FALSE; I_ShutdownConsole(); M_SaveConfig(NULL); //save game config, cvars.. -#ifndef NONET - D_SaveBan(); // save the ban list -#endif // Make sure you lose points for ALT-F4 if (Playing()) @@ -3146,9 +3143,6 @@ void I_Error(const char *error, ...) fflush(stderr); #endif M_SaveConfig(NULL); // save game config, cvars.. -#ifndef NONET - D_SaveBan(); // save the ban list -#endif G_DirtyGameData(); // done first in case an error is in G_SaveGameData G_SaveGameData(); // Tails 12-08-2002 diff --git a/src/typedef.h b/src/typedef.h index 12160450d..79527c2d6 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -18,8 +18,11 @@ extern "C" { #endif +#define TYPEDEF3(tag, id, new_type) \ + typedef tag id new_type + #define TYPEDEF2(struct_id, new_type) \ - typedef struct struct_id new_type + TYPEDEF3 (struct, struct_id, new_type) #define TYPEDEF(id) TYPEDEF2 (id, id) @@ -163,6 +166,9 @@ TYPEDEF (JoyFF_t); // i_time.h TYPEDEF (timestate_t); +// i_tcp.h +TYPEDEF3 (union, mysockaddr_t, mysockaddr_t); + // info.h TYPEDEF (state_t); TYPEDEF (mobjinfo_t); @@ -204,9 +210,12 @@ TYPEDEF (pathfindsetup_t); // k_profiles.h TYPEDEF (profile_t); -// h_serverstats.h +// k_serverstats.h TYPEDEF (serverplayer_t); +// k_bans.h +TYPEDEF (banrecord_t); + // k_terrain.h TYPEDEF (t_splash_t); TYPEDEF (t_footstep_t);